From: Joseph Lo <josephl@nvidia.com>
To: Dmitry Osipenko <digetx@gmail.com>,
Thierry Reding <thierry.reding@gmail.com>,
Peter De Schrijver <pdeschrijver@nvidia.com>,
Jonathan Hunter <jonathanh@nvidia.com>,
Rob Herring <robh+dt@kernel.org>, Stephen Boyd <sboyd@kernel.org>
Cc: <linux-tegra@vger.kernel.org>, <devicetree@vger.kernel.org>,
<linux-clk@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>
Subject: Re: [PATCH 5/8] memory: tegra: Add EMC scaling sequence code for Tegra210
Date: Tue, 2 Apr 2019 22:49:04 +0800 [thread overview]
Message-ID: <aa0be43b-f1fd-e7dc-3275-d7a73fb6f48e@nvidia.com> (raw)
In-Reply-To: <2ff88007-8c37-3775-88a1-69d3a38c01a9@gmail.com>
On 4/2/19 7:36 PM, Dmitry Osipenko wrote:
> 25.03.2019 10:45, Joseph Lo пишет:
>> This patch includes the sequence for controlling the rate changing for
>> EMC frequency and the dynamic training mechanism when the rate reaches
>> the higher rates of EMC rate.
>>
>> And historically there have been different sequences to change the EMC
>> clock. The sequence to be used is specified in the scaling data.
>> However, for the currently supported upstreaming platform, only the most
>> recent sequence is used. So only support that in this patch.
>>
>> Based on the work of Peter De Schrijver <pdeschrijver@nvidia.com>.
>>
>> Signed-off-by: Joseph Lo <josephl@nvidia.com>
>> ---
>> drivers/memory/tegra/Makefile | 2 +-
>> drivers/memory/tegra/tegra210-emc-cc-r21021.c | 1962 +++++++++++++++++
>> drivers/memory/tegra/tegra210-emc-reg.h | 134 ++
>> drivers/memory/tegra/tegra210-emc.c | 5 +
>> 4 files changed, 2102 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/memory/tegra/tegra210-emc-cc-r21021.c
>>
>> diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
>> index 36a835620bbd..dcc245b2ef45 100644
>> --- a/drivers/memory/tegra/Makefile
>> +++ b/drivers/memory/tegra/Makefile
>> @@ -12,5 +12,5 @@ obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
>>
>> obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
>> obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
>> -obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o tegra210-dt-parse.o
>> +obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o tegra210-dt-parse.o tegra210-emc-cc-r21021.o
>> obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
>> diff --git a/drivers/memory/tegra/tegra210-emc-cc-r21021.c b/drivers/memory/tegra/tegra210-emc-cc-r21021.c
>> new file mode 100644
>> index 000000000000..f577a8c3aa95
>> --- /dev/null
>> +++ b/drivers/memory/tegra/tegra210-emc-cc-r21021.c
>> @@ -0,0 +1,1962 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/io.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/of.h>
>> +#include <soc/tegra/mc.h>
>> +
>> +#include "mc.h"
>> +#include "tegra210-emc-reg.h"
>> +
>> +#define DVFS_CLOCK_CHANGE_VERSION 21021
>> +#define EMC_PRELOCK_VERSION 2101
>> +
>> +#define emc_cc_dbg(t, ...) pr_debug(__VA_ARGS__)
>> +
>> +/*
>> + * Enable flags for specifying verbosity.
>> + */
>> +#define INFO (1 << 0)
>> +#define STEPS (1 << 1)
>> +#define SUB_STEPS (1 << 2)
>> +#define PRELOCK (1 << 3)
>> +#define PRELOCK_STEPS (1 << 4)
>> +#define ACTIVE_EN (1 << 5)
>> +#define PRAMP_UP (1 << 6)
>> +#define PRAMP_DN (1 << 7)
>> +#define EMA_WRITES (1 << 10)
>> +#define EMA_UPDATES (1 << 11)
>> +#define PER_TRAIN (1 << 16)
>> +#define CC_PRINT (1 << 17)
>> +#define CCFIFO (1 << 29)
>> +#define REGS (1 << 30)
>> +#define REG_LISTS (1 << 31)
>> +
>> +enum {
>> + DVFS_SEQUENCE = 1,
>> + WRITE_TRAINING_SEQUENCE = 2,
>> + PERIODIC_TRAINING_SEQUENCE = 3,
>> + DVFS_PT1 = 10,
>> + DVFS_UPDATE = 11,
>> + TRAINING_PT1 = 12,
>> + TRAINING_UPDATE = 13,
>> + PERIODIC_TRAINING_UPDATE = 14
>> +};
>> +
>> +/*
>> + * PTFV defines - basically just indexes into the per table PTFV array.
>> + */
>> +#define PTFV_DQSOSC_MOVAVG_C0D0U0_INDEX 0
>> +#define PTFV_DQSOSC_MOVAVG_C0D0U1_INDEX 1
>> +#define PTFV_DQSOSC_MOVAVG_C0D1U0_INDEX 2
>> +#define PTFV_DQSOSC_MOVAVG_C0D1U1_INDEX 3
>> +#define PTFV_DQSOSC_MOVAVG_C1D0U0_INDEX 4
>> +#define PTFV_DQSOSC_MOVAVG_C1D0U1_INDEX 5
>> +#define PTFV_DQSOSC_MOVAVG_C1D1U0_INDEX 6
>> +#define PTFV_DQSOSC_MOVAVG_C1D1U1_INDEX 7
>> +#define PTFV_DVFS_SAMPLES_INDEX 9
>> +#define PTFV_MOVAVG_WEIGHT_INDEX 10
>> +#define PTFV_CONFIG_CTRL_INDEX 11
>> +
>> +#define PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA (1 << 0)
>> +
>> +/*
>> + * Do arithmetic in fixed point.
>> + */
>> +#define MOVAVG_PRECISION_FACTOR 100
>> +
>> +/*
>> + * The division portion of the average operation.
>> + */
>> +#define __AVERAGE_PTFV(dev) \
>> + ({ next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \
>> + next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
>> + next_timing->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
>> +
>> +/*
>> + * Convert val to fixed point and add it to the temporary average.
>> + */
>> +#define __INCREMENT_PTFV(dev, val) \
>> + ({ next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \
>> + ((val) * MOVAVG_PRECISION_FACTOR); })
>> +
>> +/*
>> + * Convert a moving average back to integral form and return the value.
>> + */
>> +#define __MOVAVG_AC(timing, dev) \
>> + ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
>> + MOVAVG_PRECISION_FACTOR)
>> +
>> +/* Weighted update. */
>> +#define __WEIGHTED_UPDATE_PTFV(dev, nval) \
>> + do { \
>> + int w = PTFV_MOVAVG_WEIGHT_INDEX; \
>> + int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX; \
>> + \
>> + next_timing->ptfv_list[dqs] = \
>> + ((nval * MOVAVG_PRECISION_FACTOR) + \
>> + (next_timing->ptfv_list[dqs] * \
>> + next_timing->ptfv_list[w])) / \
>> + (next_timing->ptfv_list[w] + 1); \
>> + \
>> + emc_cc_dbg(EMA_UPDATES, "%s: (s=%u) EMA: %u\n", \
>> + __stringify(dev), nval, \
>> + next_timing->ptfv_list[dqs]); \
>> + } while (0)
>> +
>> +/* Access a particular average. */
>> +#define __MOVAVG(timing, dev) \
>> + ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX])
>> +
>> +static u32 update_clock_tree_delay(struct tegra_emc *emc,
>> + u32 dram_dev_num, u32 channel_mode, int type)
>> +{
>> + u32 mrr_req = 0, mrr_data = 0;
>> + u32 temp0_0 = 0, temp0_1 = 0, temp1_0 = 0, temp1_1 = 0;
>> + s32 tdel = 0, tmdel = 0, adel = 0;
>> + u32 cval = 0;
>> + struct emc_table *last_timing = emc->current_timing;
>> + struct emc_table *next_timing = emc->next_timing;
>> + u32 last_timing_rate_mhz = last_timing->rate / 1000;
>> + u32 next_timing_rate_mhz = next_timing->rate / 1000;
>> + int dvfs_pt1 = type == DVFS_PT1;
>> + int dvfs_update = type == DVFS_UPDATE;
>> + int periodic_training_update = type == PERIODIC_TRAINING_UPDATE;
>> +
>> + /*
>> + * Dev0 MSB.
>> + */
>> + if (dvfs_pt1 || periodic_training_update) {
>> + mrr_req = (2 << EMC_MRR_DEV_SEL_SHIFT) |
>> + (19 << EMC_MRR_MA_SHIFT);
>> + emc_writel(emc, mrr_req, EMC_MRR);
>> +
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
>> + "Timed out waiting for MRR 19 (ch=0)\n");
>> + if (channel_mode == DUAL_CHANNEL)
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1,
>> + REG_EMC1),
>> + "Timed out waiting for MRR 19 (ch=1)\n");
>> +
>> + mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> +
>> + temp0_0 = (mrr_data & 0xff) << 8;
>> + temp0_1 = mrr_data & 0xff00;
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + mrr_data = (emc1_readl(emc, EMC_MRR) &
>> + EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> + temp1_0 = (mrr_data & 0xff) << 8;
>> + temp1_1 = mrr_data & 0xff00;
>> + }
>> +
>> + /*
>> + * Dev0 LSB.
>> + */
>> + mrr_req = (mrr_req & ~EMC_MRR_MA_MASK) |
>> + (18 << EMC_MRR_MA_SHIFT);
>> + emc_writel(emc, mrr_req, EMC_MRR);
>> +
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
>> + "Timed out waiting for MRR 18 (ch=0)\n");
>> + if (channel_mode == DUAL_CHANNEL)
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1,
>> + REG_EMC1),
>> + "Timed out waiting for MRR 18 (ch=1)\n");
>> +
>> + mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> +
>> + temp0_0 |= mrr_data & 0xff;
>> + temp0_1 |= (mrr_data & 0xff00) >> 8;
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + mrr_data = (emc1_readl(emc, EMC_MRR) &
>> + EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> + temp1_0 |= (mrr_data & 0xff);
>> + temp1_1 |= (mrr_data & 0xff00) >> 8;
>> + }
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp0_0);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C0D0U0, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C0D0U0);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C0D0U0, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c0d0u0 -
>> + __MOVAVG_AC(next_timing, C0D0U0);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> + adel = tmdel;
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c0d0u0 =
>> + __MOVAVG_AC(next_timing, C0D0U0);
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp0_1);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C0D0U1, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C0D0U1);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C0D0U1, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c0d0u1 -
>> + __MOVAVG_AC(next_timing, C0D0U1);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> +
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c0d0u1 =
>> + __MOVAVG_AC(next_timing, C0D0U1);
>> + }
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp1_0);
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C1D0U0, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C1D0U0);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C1D0U0, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c1d0u0 -
>> + __MOVAVG_AC(next_timing, C1D0U0);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> +
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c1d0u0 =
>> + __MOVAVG_AC(next_timing, C1D0U0);
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp1_1);
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C1D0U1, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C1D0U1);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C1D0U1, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c1d0u1 -
>> + __MOVAVG_AC(next_timing, C1D0U1);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> +
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c1d0u1 =
>> + __MOVAVG_AC(next_timing, C1D0U1);
>> + }
>> + }
>> +
>> + if (emc->dram_dev_num != TWO_RANK)
>> + goto done;
>> +
>> + /*
>> + * Dev1 MSB.
>> + */
>> + if (dvfs_pt1 || periodic_training_update) {
>> + mrr_req = (1 << EMC_MRR_DEV_SEL_SHIFT) |
>> + (19 << EMC_MRR_MA_SHIFT);
>> + emc_writel(emc, mrr_req, EMC_MRR);
>> +
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
>> + "Timed out waiting for MRR 19 (ch=0)\n");
>> + if (channel_mode == DUAL_CHANNEL)
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1,
>> + REG_EMC1),
>> + "Timed out waiting for MRR 19 (ch=1)\n");
>> +
>> + mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> +
>> + temp0_0 = (mrr_data & 0xff) << 8;
>> + temp0_1 = mrr_data & 0xff00;
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + mrr_data = (emc1_readl(emc, EMC_MRR) &
>> + EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> + temp1_0 = (mrr_data & 0xff) << 8;
>> + temp1_1 = mrr_data & 0xff00;
>> + }
>> +
>> + /*
>> + * Dev1 LSB.
>> + */
>> + mrr_req = (mrr_req & ~EMC_MRR_MA_MASK) |
>> + (18 << EMC_MRR_MA_SHIFT);
>> + emc_writel(emc, mrr_req, EMC_MRR);
>> +
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
>> + "Timed out waiting for MRR 18 (ch=0)\n");
>> + if (channel_mode == DUAL_CHANNEL)
>> + WARN(wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_MRR_DIVLD, 1,
>> + REG_EMC1),
>> + "Timed out waiting for MRR 18 (ch=1)\n");
>> +
>> + mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> +
>> + temp0_0 |= mrr_data & 0xff;
>> + temp0_1 |= (mrr_data & 0xff00) >> 8;
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + mrr_data = (emc1_readl(emc, EMC_MRR) &
>> + EMC_MRR_DATA_MASK) <<
>> + EMC_MRR_DATA_SHIFT;
>> + temp1_0 |= (mrr_data & 0xff);
>> + temp1_1 |= (mrr_data & 0xff00) >> 8;
>> + }
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp0_0);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C0D1U0, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C0D1U0);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C0D1U0, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c0d1u0 -
>> + __MOVAVG_AC(next_timing, C0D1U0);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c0d1u0 =
>> + __MOVAVG_AC(next_timing, C0D1U0);
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp0_1);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C0D1U1, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C0D1U1);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C0D1U1, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c0d1u1 -
>> + __MOVAVG_AC(next_timing, C0D1U1);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c0d1u1 =
>> + __MOVAVG_AC(next_timing, C0D1U1);
>> + }
>> +
>> + if (channel_mode == DUAL_CHANNEL) {
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp1_0);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C1D1U0, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C1D1U0);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C1D1U0, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c1d1u0 -
>> + __MOVAVG_AC(next_timing, C1D1U0);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c1d1u0 =
>> + __MOVAVG_AC(next_timing, C1D1U0);
>> + }
>> +
>> + if (dvfs_pt1 || periodic_training_update)
>> + cval = (1000000 * tegra210_actual_osc_clocks(
>> + last_timing->run_clocks)) /
>> + (last_timing_rate_mhz * 2 * temp1_1);
>> +
>> + if (dvfs_pt1)
>> + __INCREMENT_PTFV(C1D1U1, cval);
>> + else if (dvfs_update)
>> + __AVERAGE_PTFV(C1D1U1);
>> + else if (periodic_training_update)
>> + __WEIGHTED_UPDATE_PTFV(C1D1U1, cval);
>> +
>> + if (dvfs_update || periodic_training_update) {
>> + tdel = next_timing->current_dram_clktree_c1d1u1 -
>> + __MOVAVG_AC(next_timing, C1D1U1);
>> + tmdel = (tdel < 0) ? -1 * tdel : tdel;
>> + if (tmdel > adel)
>> + adel = tmdel;
>> +
>> + if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
>> + next_timing->tree_margin)
>> + next_timing->current_dram_clktree_c1d1u1 =
>> + __MOVAVG_AC(next_timing, C1D1U1);
>> + }
>> + }
>> +
>> +done:
>> + return adel;
>> +}
>> +
>> +static u32 periodic_compensation_handler(struct tegra_emc *emc, u32 type,
>> + u32 dram_dev_num,
>> + u32 channel_mode,
>> + struct emc_table *last_timing,
>> + struct emc_table *next_timing)
>> +{
>> +#define __COPY_EMA(nt, lt, dev) \
>> + ({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) * \
>> + (nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
>> +
>> + u32 i;
>> + u32 adel = 0;
>> + u32 samples = next_timing->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
>> + u32 delay = 2 +
>> + (1000 * tegra210_actual_osc_clocks(last_timing->run_clocks) /
>> + last_timing->rate);
>> +
>> + if (!next_timing->periodic_training)
>> + return 0;
>> +
>> + if (type == DVFS_SEQUENCE) {
>> + if (last_timing->periodic_training &&
>> + (next_timing->ptfv_list[PTFV_CONFIG_CTRL_INDEX] &
>> + PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA)) {
>> + /*
>> + * If the previous frequency was using periodic
>> + * calibration then we can reuse the previous
>> + * frequencies EMA data.
>> + */
>> + __COPY_EMA(next_timing, last_timing, C0D0U0);
>> + __COPY_EMA(next_timing, last_timing, C0D0U1);
>> + __COPY_EMA(next_timing, last_timing, C1D0U0);
>> + __COPY_EMA(next_timing, last_timing, C1D0U1);
>> + __COPY_EMA(next_timing, last_timing, C0D1U0);
>> + __COPY_EMA(next_timing, last_timing, C0D1U1);
>> + __COPY_EMA(next_timing, last_timing, C1D1U0);
>> + __COPY_EMA(next_timing, last_timing, C1D1U1);
>> + } else {
>> + /* Reset the EMA.*/
>> + __MOVAVG(next_timing, C0D0U0) = 0;
>> + __MOVAVG(next_timing, C0D0U1) = 0;
>> + __MOVAVG(next_timing, C1D0U0) = 0;
>> + __MOVAVG(next_timing, C1D0U1) = 0;
>> + __MOVAVG(next_timing, C0D1U0) = 0;
>> + __MOVAVG(next_timing, C0D1U1) = 0;
>> + __MOVAVG(next_timing, C1D1U0) = 0;
>> + __MOVAVG(next_timing, C1D1U1) = 0;
>> +
>> + for (i = 0; i < samples; i++) {
>> + tegra210_start_periodic_compensation(emc);
>> + udelay(delay);
>> +
>> + /*
>> + * Generate next sample of data.
>> + */
>> + adel = update_clock_tree_delay(emc,
>> + emc->dram_dev_num,
>> + channel_mode,
>> + DVFS_PT1);
>> + }
>> + }
>> +
>> + /*
>> + * Seems like it should be part of the
>> + * 'if (last_timing->periodic_training)' conditional
>> + * since is already done for the else clause.
>> + */
>> + adel = update_clock_tree_delay(emc,
>> + emc->dram_dev_num,
>> + channel_mode,
>> + DVFS_UPDATE);
>> + }
>> +
>> + if (type == PERIODIC_TRAINING_SEQUENCE) {
>> + tegra210_start_periodic_compensation(emc);
>> + udelay(delay);
>> +
>> + adel = update_clock_tree_delay(emc,
>> + emc->dram_dev_num,
>> + channel_mode,
>> + PERIODIC_TRAINING_UPDATE);
>> + }
>> +
>> + return adel;
>> +}
>> +
>> +u32 __do_periodic_emc_compensation_r21021(struct tegra_emc *emc)
>> +{
>> + u32 dram_dev_num;
>> + u32 channel_mode;
>> + u32 emc_cfg, emc_cfg_o;
>> + u32 emc_dbg_o;
>> + u32 del, i;
>> + u32 list[] = {
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2,
>> + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3,
>> + EMC_DATA_BRLSHFT_0,
>> + EMC_DATA_BRLSHFT_1
>> + };
>> + u32 items = ARRAY_SIZE(list);
>> + u32 emc_cfg_update;
>> + struct emc_table *current_timing = emc->current_timing;
>> +
>> + if (current_timing->periodic_training) {
>> + channel_mode =
>> + !!(current_timing->burst_regs[EMC_FBIO_CFG7_INDEX] &
>> + (1 << 2));
>> + dram_dev_num = 1 + (mc_readl(emc->mc, MC_EMEM_ADR_CFG) & 0x1);
>> +
>> + emc_cc_dbg(PER_TRAIN, "Periodic training starting\n");
>> +
>> + emc_dbg_o = emc_readl(emc, EMC_DBG);
>> + emc_cfg_o = emc_readl(emc, EMC_CFG);
>> + emc_cfg = emc_cfg_o & ~(EMC_CFG_DYN_SELF_REF |
>> + EMC_CFG_DRAM_ACPD |
>> + EMC_CFG_DRAM_CLKSTOP_PD |
>> + EMC_CFG_DRAM_CLKSTOP_PD);
>> +
>> +
>> + /*
>> + * 1. Power optimizations should be off.
>> + */
>> + emc_writel(emc, emc_cfg, EMC_CFG);
>> +
>> + /* Does emc_timing_update() for above changes. */
>> + tegra210_dll_disable(emc, channel_mode);
>> +
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0,
>> + REG_EMC);
>> + if (channel_mode)
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK,
>> + 0, REG_EMC1);
>> +
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0,
>> + REG_EMC);
>> + if (channel_mode)
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0,
>> + REG_EMC1);
>> +
>> + emc_cfg_update = emc_readl(emc, EMC_CFG_UPDATE);
>> + emc_writel(emc, (emc_cfg_update &
>> + ~EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK) |
>> + (2 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT),
>> + EMC_CFG_UPDATE);
>> +
>> + /*
>> + * 2. osc kick off - this assumes training and dvfs have set
>> + * correct MR23.
>> + */
>> + tegra210_start_periodic_compensation(emc);
>> +
>> + /*
>> + * 3. Let dram capture its clock tree delays.
>> + */
>> + udelay((tegra210_actual_osc_clocks(current_timing->run_clocks) *
>> + 1000) /
>> + current_timing->rate + 1);
>> +
>> + /*
>> + * 4. Check delta wrt previous values (save value if margin
>> + * exceeds what is set in table).
>> + */
>> + del = periodic_compensation_handler(emc,
>> + PERIODIC_TRAINING_SEQUENCE,
>> + dram_dev_num,
>> + channel_mode,
>> + current_timing,
>> + current_timing);
>> +
>> + /*
>> + * 5. Apply compensation w.r.t. trained values (if clock tree
>> + * has drifted more than the set margin).
>> + */
>> + if (current_timing->tree_margin <
>> + ((del * 128 * (current_timing->rate / 1000)) / 1000000)) {
>> + for (i = 0; i < items; i++) {
>> + u32 tmp =
>> + tegra210_apply_periodic_compensation_trimmer(
>> + current_timing, list[i]);
>> +
>> + emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
>> + list[i], tmp);
>> + emc_writel(emc, tmp, list[i]);
>> + }
>> + }
>> +
>> + emc_writel(emc, emc_cfg_o, EMC_CFG);
>> +
>> + /*
>> + * 6. Timing update actally applies the new trimmers.
>> + */
>> + emc_timing_update(emc, channel_mode);
>> +
>> + /* 6.1. Restore the UPDATE_DLL_IN_UPDATE field. */
>> + emc_writel(emc, emc_cfg_update, EMC_CFG_UPDATE);
>> +
>> + /* 6.2. Restore the DLL. */
>> + tegra210_dll_enable(emc, channel_mode);
>> +
>> + /*
>> + * 7. Copy over the periodic training registers that we updated
>> + * here to the corresponding derated/non-derated table.
>> + */
>> + tegra210_update_emc_alt_timing(emc, current_timing);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * Do the clock change sequence.
>> + */
>> +void emc_set_clock_r21021(struct tegra_emc *emc, u32 clksrc)
>> +{
>> + /*
>> + * This is the timing table for the source frequency. It does _not_
>> + * necessarily correspond to the actual timing values in the EMC at the
>> + * moment. If the boot BCT differs from the table then this can happen.
>> + * However, we need it for accessing the dram_timings (which are not
>> + * really registers) array for the current frequency.
>> + */
>> + struct emc_table *fake_timing;
>> + struct emc_table *last_timing = emc->current_timing;
>> + struct emc_table *next_timing = emc->next_timing;
>> +
>> + u32 i, tmp;
>> +
>> + u32 cya_allow_ref_cc = 0, ref_b4_sref_en = 0, cya_issue_pc_ref = 0;
>> +
>> + u32 zqcal_before_cc_cutoff = 2400; /* In picoseconds */
>> + u32 ref_delay_mult;
>> + u32 ref_delay;
>> + s32 zq_latch_dvfs_wait_time;
>> + s32 tZQCAL_lpddr4_fc_adj;
>> + /* Scaled by x1000 */
>> + u32 tFC_lpddr4 = 1000 * next_timing->dram_timings[T_FC_LPDDR4];
>> + u32 tZQCAL_lpddr4 = 1000000;
>> +
>> + u32 dram_type, dram_dev_num, shared_zq_resistor;
>> + u32 channel_mode;
>> + u32 is_lpddr3;
>> +
>> + u32 emc_cfg, emc_sel_dpd_ctrl, emc_cfg_reg;
>> +
>> + u32 emc_dbg;
>> + u32 emc_zcal_interval;
>> + u32 emc_zcal_wait_cnt_old;
>> + u32 emc_zcal_wait_cnt_new;
>> + u32 emc_dbg_active;
>> + u32 zq_op;
>> + u32 zcal_wait_time_clocks;
>> + u32 zcal_wait_time_ps;
>> +
>> + u32 emc_auto_cal_config;
>> + u32 auto_cal_en;
>> +
>> + u32 mr13_catr_enable;
>> +
>> + u32 ramp_up_wait = 0, ramp_down_wait = 0;
>> +
>> + /* In picoseconds. */
>> + u32 source_clock_period;
>> + u32 destination_clock_period;
>> +
>> + u32 emc_dbg_o;
>> + u32 emc_cfg_pipe_clk_o;
>> + u32 emc_pin_o;
>> +
>> + u32 mr13_flip_fspwr;
>> + u32 mr13_flip_fspop;
>> +
>> + u32 opt_zcal_en_cc;
>> + u32 opt_do_sw_qrst = 1;
>> + u32 opt_dvfs_mode;
>> + u32 opt_dll_mode;
>> + u32 opt_cc_short_zcal = 1;
>> + u32 opt_short_zcal = 1;
>> + u32 save_restore_clkstop_pd = 1;
>> +
>> + u32 prelock_dll_en = 0, dll_out;
>> +
>> + int next_push, next_dq_e_ivref, next_dqs_e_ivref;
>> +
>> + u32 opt_war_200024907;
>> + u32 zq_wait_long;
>> + u32 zq_wait_short;
>> +
>> + u32 bg_regulator_switch_complete_wait_clks;
>> + u32 bg_regulator_mode_change;
>> + u32 enable_bglp_regulator;
>> + u32 enable_bg_regulator;
>> +
>> + u32 tRTM;
>> + u32 RP_war;
>> + u32 R2P_war;
>> + u32 TRPab_war;
>> + s32 nRTP;
>> + u32 deltaTWATM;
>> + u32 W2P_war;
>> + u32 tRPST;
>> +
>> + u32 mrw_req;
>> + u32 adel = 0, compensate_trimmer_applicable = 0;
>> + u32 next_timing_rate_mhz = next_timing->rate / 1000;
>> +
>> + static u32 fsp_for_next_freq;
>> +
>> + emc_cc_dbg(INFO, "Running clock change.\n");
>> +
>> + fake_timing = get_timing_from_freq(emc, last_timing->rate);
>> +
>> + fsp_for_next_freq = !fsp_for_next_freq;
>> +
>> + dram_type = emc_readl(emc, EMC_FBIO_CFG5) &
>> + EMC_FBIO_CFG5_DRAM_TYPE_MASK >>
>> + EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
>> + shared_zq_resistor = last_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX] &
>> + 1 << 31;
>> + channel_mode = !!(last_timing->burst_regs[EMC_FBIO_CFG7_INDEX] &
>> + 1 << 2);
>> + opt_zcal_en_cc = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] &&
>> + !last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX]) ||
>> + dram_type == DRAM_TYPE_LPDDR4;
>> + opt_dll_mode = (dram_type == DRAM_TYPE_DDR3) ?
>> + get_dll_state(next_timing) : DLL_OFF;
>> + is_lpddr3 = (dram_type == DRAM_TYPE_LPDDR2) &&
>> + next_timing->burst_regs[EMC_FBIO_CFG5_INDEX] &
>> + 1 << 25;
>> + opt_war_200024907 = (dram_type == DRAM_TYPE_LPDDR4);
>> + opt_dvfs_mode = MAN_SR;
>> + dram_dev_num = (mc_readl(emc->mc, MC_EMEM_ADR_CFG) & 0x1) + 1;
>> +
>> + emc_cfg_reg = emc_readl(emc, EMC_CFG);
>> + emc_auto_cal_config = emc_readl(emc, EMC_AUTO_CAL_CONFIG);
>> +
>> + source_clock_period = 1000000000 / last_timing->rate;
>> + destination_clock_period = 1000000000 / next_timing->rate;
>> +
>> + tZQCAL_lpddr4_fc_adj = (destination_clock_period >
>> + zqcal_before_cc_cutoff) ?
>> + tZQCAL_lpddr4 / destination_clock_period :
>> + (tZQCAL_lpddr4 - tFC_lpddr4) / destination_clock_period;
>> + emc_dbg_o = emc_readl(emc, EMC_DBG);
>> + emc_pin_o = emc_readl(emc, EMC_PIN);
>> + emc_cfg_pipe_clk_o = emc_readl(emc, EMC_CFG_PIPE_CLK);
>> + emc_dbg = emc_dbg_o;
>> +
>> + emc_cfg = next_timing->burst_regs[EMC_CFG_INDEX];
>> + emc_cfg &= ~(EMC_CFG_DYN_SELF_REF | EMC_CFG_DRAM_ACPD |
>> + EMC_CFG_DRAM_CLKSTOP_SR | EMC_CFG_DRAM_CLKSTOP_PD);
>> + emc_sel_dpd_ctrl = next_timing->emc_sel_dpd_ctrl;
>> + emc_sel_dpd_ctrl &= ~(EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN |
>> + EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN |
>> + EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN |
>> + EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN |
>> + EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN);
>> +
>> + emc_cc_dbg(INFO, "Clock change version: %d\n",
>> + DVFS_CLOCK_CHANGE_VERSION);
>> + emc_cc_dbg(INFO, "DRAM type = %d\n", emc->dram_type);
>> + emc_cc_dbg(INFO, "DRAM dev #: %d\n", dram_dev_num);
>> + emc_cc_dbg(INFO, "Next EMC clksrc: 0x%08x\n", clksrc);
>> + emc_cc_dbg(INFO, "DLL clksrc: 0x%08x\n", next_timing->dll_clk_src);
>> + emc_cc_dbg(INFO, "last rate: %u, next rate %u\n", last_timing->rate,
>> + next_timing->rate);
>> + emc_cc_dbg(INFO, "last period: %u, next period: %u\n",
>> + source_clock_period, destination_clock_period);
>> + emc_cc_dbg(INFO, " shared_zq_resistor: %d\n", !!shared_zq_resistor);
>> + emc_cc_dbg(INFO, " channel_mode: %d\n", channel_mode);
>> + emc_cc_dbg(INFO, " opt_dll_mode: %d\n", opt_dll_mode);
>> +
>> + /*
>> + * Step 1:
>> + * Pre DVFS SW sequence.
>> + */
>> + emc_cc_dbg(STEPS, "Step 1\n");
>> + emc_cc_dbg(STEPS, "Step 1.1: Disable DLL temporarily.\n");
>> + tmp = emc_readl(emc, EMC_CFG_DIG_DLL);
>> + tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN;
>> + emc_writel(emc, tmp, EMC_CFG_DIG_DLL);
>> +
>> + emc_timing_update(emc, channel_mode);
>> + wait_for_update(emc, EMC_CFG_DIG_DLL,
>> + EMC_CFG_DIG_DLL_CFG_DLL_EN, 0, REG_EMC);
>> + if (channel_mode)
>> + wait_for_update(emc, EMC_CFG_DIG_DLL,
>> + EMC_CFG_DIG_DLL_CFG_DLL_EN, 0, REG_EMC1);
>> +
>> + emc_cc_dbg(STEPS, "Step 1.2: Disable AUTOCAL temporarily.\n");
>> + emc_auto_cal_config = next_timing->emc_auto_cal_config;
>> + auto_cal_en = emc_auto_cal_config & EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
>> + emc_auto_cal_config &= ~EMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
>> + emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL;
>> + emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL;
>> + emc_auto_cal_config |= auto_cal_en;
>> + emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG);
>> + emc_readl(emc, EMC_AUTO_CAL_CONFIG); /* Flush write. */
>> +
>> + emc_cc_dbg(STEPS, "Step 1.3: Disable other power features.\n");
>> + emc_set_shadow_bypass(emc, ACTIVE);
>> + emc_writel(emc, emc_cfg, EMC_CFG);
>> + emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL);
>> + emc_set_shadow_bypass(emc, ASSEMBLY);
>> +
>> + if (next_timing->periodic_training) {
>> + tegra210_reset_dram_clktree_values(next_timing);
>> +
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0,
>> + REG_EMC);
>> + if (channel_mode)
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK,
>> + 0, REG_EMC1);
>> +
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0,
>> + REG_EMC);
>> + if (channel_mode)
>> + wait_for_update(emc, EMC_EMC_STATUS,
>> + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0,
>> + REG_EMC1);
>> +
>> + tegra210_start_periodic_compensation(emc);
>> +
>> + udelay(((1000 *
>> + tegra210_actual_osc_clocks(last_timing->run_clocks)) /
>> + last_timing->rate) + 2);
>> + adel = periodic_compensation_handler(emc, DVFS_SEQUENCE,
>> + dram_dev_num,
>> + channel_mode,
>> + fake_timing, next_timing);
>> + compensate_trimmer_applicable =
>> + next_timing->periodic_training &&
>> + ((adel * 128 * next_timing_rate_mhz) / 1000000) >
>> + next_timing->tree_margin;
>> + }
>> +
>> + emc_writel(emc, EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS);
>> + emc_set_shadow_bypass(emc, ACTIVE);
>> + emc_writel(emc, emc_cfg, EMC_CFG);
>> + emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL);
>> + emc_writel(emc, emc_cfg_pipe_clk_o | EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON,
>> + EMC_CFG_PIPE_CLK);
>> + emc_writel(emc, next_timing->emc_fdpd_ctrl_cmd_no_ramp &
>> + ~EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE,
>> + EMC_FDPD_CTRL_CMD_NO_RAMP);
>> +
>> + bg_regulator_mode_change =
>> + ((next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) ^
>> + (last_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD)) ||
>> + ((next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) ^
>> + (last_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD));
>> + enable_bglp_regulator =
>> + (next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) == 0;
>> + enable_bg_regulator =
>> + (next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) == 0;
>> +
>> + if (bg_regulator_mode_change) {
>> + if (enable_bg_regulator)
>> + emc_writel(emc, last_timing->burst_regs
>> + [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD,
>> + EMC_PMACRO_BG_BIAS_CTRL_0);
>> + else
>> + emc_writel(emc, last_timing->burst_regs
>> + [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
>> + ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD,
>> + EMC_PMACRO_BG_BIAS_CTRL_0);
>> + }
>> +
>> + /* Check if we need to turn on VREF generator. */
>> + if ((((last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 0) &&
>> + ((next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 1)) ||
>> + (((last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 0) &&
>> + ((next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 1))) {
>> + u32 pad_tx_ctrl =
>> + next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
>> + u32 last_pad_tx_ctrl =
>> + last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
>> +
>> + next_dqs_e_ivref = pad_tx_ctrl &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF;
>> + next_dq_e_ivref = pad_tx_ctrl &
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF;
>> + next_push = (last_pad_tx_ctrl &
>> + ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF &
>> + ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) |
>> + next_dq_e_ivref | next_dqs_e_ivref;
>> + emc_writel(emc, next_push, EMC_PMACRO_DATA_PAD_TX_CTRL);
>> + udelay(1);
>> + } else if (bg_regulator_mode_change) {
>> + udelay(1);
>> + }
>> +
>> + emc_set_shadow_bypass(emc, ASSEMBLY);
>> +
>> + /*
>> + * Step 2:
>> + * Prelock the DLL.
>> + */
>> + emc_cc_dbg(STEPS, "Step 2\n");
>> + if (next_timing->burst_regs[EMC_CFG_DIG_DLL_INDEX] &
>> + EMC_CFG_DIG_DLL_CFG_DLL_EN) {
>> + emc_cc_dbg(INFO, "Prelock enabled for target frequency.\n");
>> + dll_out = tegra210_dll_prelock(emc, 0, clksrc);
>> + emc_cc_dbg(INFO, "DLL out: 0x%03x\n", dll_out);
>> + prelock_dll_en = 1;
>> + } else {
>> + emc_cc_dbg(INFO, "Disabling DLL for target frequency.\n");
>> + tegra210_dll_disable(emc, channel_mode);
>> + }
>> +
>> + /*
>> + * Step 3:
>> + * Prepare autocal for the clock change.
>> + */
>> + emc_cc_dbg(STEPS, "Step 3\n");
>> + emc_set_shadow_bypass(emc, ACTIVE);
>> + emc_writel(emc, next_timing->emc_auto_cal_config2,
>> + EMC_AUTO_CAL_CONFIG2);
>> + emc_writel(emc, next_timing->emc_auto_cal_config3,
>> + EMC_AUTO_CAL_CONFIG3);
>> + emc_writel(emc, next_timing->emc_auto_cal_config4,
>> + EMC_AUTO_CAL_CONFIG4);
>> + emc_writel(emc, next_timing->emc_auto_cal_config5,
>> + EMC_AUTO_CAL_CONFIG5);
>> + emc_writel(emc, next_timing->emc_auto_cal_config6,
>> + EMC_AUTO_CAL_CONFIG6);
>> + emc_writel(emc, next_timing->emc_auto_cal_config7,
>> + EMC_AUTO_CAL_CONFIG7);
>> + emc_writel(emc, next_timing->emc_auto_cal_config8,
>> + EMC_AUTO_CAL_CONFIG8);
>> + emc_set_shadow_bypass(emc, ASSEMBLY);
>> +
>> + emc_auto_cal_config |= (EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START |
>> + auto_cal_en);
>> + emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG);
>> +
>> + /*
>> + * Step 4:
>> + * Update EMC_CFG. (??)
>> + */
>> + emc_cc_dbg(STEPS, "Step 4\n");
>> + if (source_clock_period > 50000 && dram_type == DRAM_TYPE_LPDDR4)
>> + ccfifo_writel(emc, 1, EMC_SELF_REF, 0);
>> + else
>> + emc_writel(emc, next_timing->emc_cfg_2, EMC_CFG_2);
>> +
>> + /*
>> + * Step 5:
>> + * Prepare reference variables for ZQCAL regs.
>> + */
>> + emc_cc_dbg(STEPS, "Step 5\n");
>> + emc_zcal_interval = 0;
>> + emc_zcal_wait_cnt_old =
>> + last_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX];
>> + emc_zcal_wait_cnt_new =
>> + next_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX];
>> + emc_zcal_wait_cnt_old &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK;
>> + emc_zcal_wait_cnt_new &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK;
>> +
>> + if (dram_type == DRAM_TYPE_LPDDR4)
>> + zq_wait_long = max((u32)1,
>> + div_o3(1000000, destination_clock_period));
>> + else if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3)
>> + zq_wait_long = max(next_timing->min_mrs_wait,
>> + div_o3(360000, destination_clock_period)) +
>> + 4;
>> + else if (dram_type == DRAM_TYPE_DDR3)
>> + zq_wait_long = max((u32)256,
>> + div_o3(320000, destination_clock_period) +
>> + 2);
>> + else
>> + zq_wait_long = 0;
>> +
>> + if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3)
>> + zq_wait_short = max(max(next_timing->min_mrs_wait, (u32)6),
>> + div_o3(90000, destination_clock_period)) +
>> + 4;
>> + else if (dram_type == DRAM_TYPE_DDR3)
>> + zq_wait_short = max((u32)64,
>> + div_o3(80000, destination_clock_period)) +
>> + 2;
>> + else
>> + zq_wait_short = 0;
>> +
>> + /*
>> + * Step 6:
>> + * Training code - removed.
>> + */
>> + emc_cc_dbg(STEPS, "Step 6\n");
>> +
>> + /*
>> + * Step 7:
>> + * Program FSP reference registers and send MRWs to new FSPWR.
>> + */
>> + emc_cc_dbg(STEPS, "Step 7\n");
>> + emc_cc_dbg(SUB_STEPS, "Step 7.1: Bug 200024907 - Patch RP R2P");
>> + if (opt_war_200024907) {
>> + nRTP = 16;
>> + if (source_clock_period >= 1000000/1866) /* 535.91 ps */
>> + nRTP = 14;
>> + if (source_clock_period >= 1000000/1600) /* 625.00 ps */
>> + nRTP = 12;
>> + if (source_clock_period >= 1000000/1333) /* 750.19 ps */
>> + nRTP = 10;
>> + if (source_clock_period >= 1000000/1066) /* 938.09 ps */
>> + nRTP = 8;
>> +
>> + deltaTWATM = max_t(u32, div_o3(7500, source_clock_period), 8);
>> +
>> + /*
>> + * Originally there was a + .5 in the tRPST calculation.
>> + * However since we can't do FP in the kernel and the tRTM
>> + * computation was in a floating point ceiling function, adding
>> + * one to tRTP should be ok. There is no other source of non
>> + * integer values, so the result was always going to be
>> + * something for the form: f_ceil(N + .5) = N + 1;
>> + */
>> + tRPST = ((last_timing->emc_mrw & 0x80) >> 7);
>> + tRTM = fake_timing->dram_timings[RL] +
>> + div_o3(3600, source_clock_period) +
>> + max_t(u32, div_o3(7500, source_clock_period), 8) +
>> + tRPST + 1 + nRTP;
>> +
>> + emc_cc_dbg(INFO, "tRTM = %u, EMC_RP = %u\n", tRTM,
>> + next_timing->burst_regs[EMC_RP_INDEX]);
>> +
>> + if (last_timing->burst_regs[EMC_RP_INDEX] < tRTM) {
>> + if (tRTM > (last_timing->burst_regs[EMC_R2P_INDEX] +
>> + last_timing->burst_regs[EMC_RP_INDEX])) {
>> + R2P_war = tRTM -
>> + last_timing->burst_regs[EMC_RP_INDEX];
>> + RP_war = last_timing->burst_regs[EMC_RP_INDEX];
>> + TRPab_war = last_timing->burst_regs[
>> + EMC_TRPAB_INDEX];
>> + if (R2P_war > 63) {
>> + RP_war = R2P_war +
>> + last_timing->burst_regs[
>> + EMC_RP_INDEX] - 63;
>> + if (TRPab_war < RP_war)
>> + TRPab_war = RP_war;
>> + R2P_war = 63;
>> + }
>> + } else {
>> + R2P_war = last_timing->burst_regs[
>> + EMC_R2P_INDEX];
>> + RP_war = last_timing->burst_regs[EMC_RP_INDEX];
>> + TRPab_war = last_timing->burst_regs[
>> + EMC_TRPAB_INDEX];
>> + }
>> +
>> + if (RP_war < deltaTWATM) {
>> + W2P_war = last_timing->burst_regs[EMC_W2P_INDEX]
>> + + deltaTWATM - RP_war;
>> + if (W2P_war > 63) {
>> + RP_war = RP_war + W2P_war - 63;
>> + if (TRPab_war < RP_war)
>> + TRPab_war = RP_war;
>> + W2P_war = 63;
>> + }
>> + } else {
>> + W2P_war = last_timing->burst_regs[
>> + EMC_W2P_INDEX];
>> + }
>> +
>> + if ((last_timing->burst_regs[EMC_W2P_INDEX] ^
>> + W2P_war) ||
>> + (last_timing->burst_regs[EMC_R2P_INDEX] ^
>> + R2P_war) ||
>> + (last_timing->burst_regs[EMC_RP_INDEX] ^
>> + RP_war) ||
>> + (last_timing->burst_regs[EMC_TRPAB_INDEX] ^
>> + TRPab_war)) {
>> + emc_writel(emc, RP_war, EMC_RP);
>> + emc_writel(emc, R2P_war, EMC_R2P);
>> + emc_writel(emc, W2P_war, EMC_W2P);
>> + emc_writel(emc, TRPab_war, EMC_TRPAB);
>> + }
>> + emc_timing_update(emc, DUAL_CHANNEL);
>> + } else {
>> + emc_cc_dbg(INFO, "Skipped WAR\n");
>> + }
>> + }
>> +
>> + if (!fsp_for_next_freq) {
>> + mr13_flip_fspwr = (next_timing->emc_mrw3 & 0xffffff3f) | 0x80;
>> + mr13_flip_fspop = (next_timing->emc_mrw3 & 0xffffff3f) | 0x00;
>> + } else {
>> + mr13_flip_fspwr = (next_timing->emc_mrw3 & 0xffffff3f) | 0x40;
>> + mr13_flip_fspop = (next_timing->emc_mrw3 & 0xffffff3f) | 0xc0;
>> + }
>> +
>> + mr13_catr_enable = (mr13_flip_fspwr & 0xFFFFFFFE) | 0x01;
>> + if (dram_dev_num == TWO_RANK)
>> + mr13_catr_enable = (mr13_catr_enable & 0x3fffffff) | 0x80000000;
>> +
>> + if (dram_type == DRAM_TYPE_LPDDR4) {
>> + emc_writel(emc, mr13_flip_fspwr, EMC_MRW3);
>> + emc_writel(emc, next_timing->emc_mrw, EMC_MRW);
>> + emc_writel(emc, next_timing->emc_mrw2, EMC_MRW2);
>> + }
>> +
>> + /*
>> + * Step 8:
>> + * Program the shadow registers.
>> + */
>> + emc_cc_dbg(STEPS, "Step 8\n");
>> + emc_cc_dbg(SUB_STEPS, "Writing burst_regs\n");
>> + for (i = 0; i < next_timing->num_burst; i++) {
>> + u32 var;
>> + u32 wval;
>> +
>> + if (!burst_regs_off[i])
>> + continue;
>> +
>> + var = burst_regs_off[i];
>> + wval = next_timing->burst_regs[i];
>> +
>> + if (dram_type != DRAM_TYPE_LPDDR4 &&
>> + (var == EMC_MRW6 || var == EMC_MRW7 ||
>> + var == EMC_MRW8 || var == EMC_MRW9 ||
>> + var == EMC_MRW10 || var == EMC_MRW11 ||
>> + var == EMC_MRW12 || var == EMC_MRW13 ||
>> + var == EMC_MRW14 || var == EMC_MRW15 ||
>> + var == EMC_TRAINING_CTRL))
>> + continue;
>> +
>> + /* Pain... And suffering. */
>> + if (var == EMC_CFG) {
>> + wval &= ~EMC_CFG_DRAM_ACPD;
>> + wval &= ~EMC_CFG_DYN_SELF_REF;
>> + if (dram_type == DRAM_TYPE_LPDDR4) {
>> + wval &= ~EMC_CFG_DRAM_CLKSTOP_SR;
>> + wval &= ~EMC_CFG_DRAM_CLKSTOP_PD;
>> + }
>> + } else if (var == EMC_MRS_WAIT_CNT &&
>> + dram_type == DRAM_TYPE_LPDDR2 &&
>> + opt_zcal_en_cc && !opt_cc_short_zcal &&
>> + opt_short_zcal) {
>> + wval = (wval & ~(EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK <<
>> + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)) |
>> + ((zq_wait_long & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) <<
>> + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT);
>> + } else if (var == EMC_ZCAL_WAIT_CNT &&
>> + dram_type == DRAM_TYPE_DDR3 && opt_zcal_en_cc &&
>> + !opt_cc_short_zcal && opt_short_zcal) {
>> + wval = (wval & ~(EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK <<
>> + EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT))
>> + | ((zq_wait_long &
>> + EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK) <<
>> + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT);
>> + } else if (var == EMC_ZCAL_INTERVAL && opt_zcal_en_cc) {
>> + wval = 0; /* EMC_ZCAL_INTERVAL reset value. */
>> + } else if (var == EMC_PMACRO_AUTOCAL_CFG_COMMON) {
>> + wval |= EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS;
>> + } else if (var == EMC_PMACRO_DATA_PAD_TX_CTRL) {
>> + wval &=
>> + ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC |
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
>> + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC);
>> + } else if (var == EMC_PMACRO_CMD_PAD_TX_CTRL) {
>> + wval |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON;
>> + wval &= ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
>> + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC |
>> + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
>> + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC);
>> + } else if (var == EMC_PMACRO_BRICK_CTRL_RFU1) {
>> + wval &= 0xf800f800;
>> + } else if (var == EMC_PMACRO_COMMON_PAD_TX_CTRL) {
>> + wval &= 0xfffffff0;
>> + }
>> +
>> + emc_writel(emc, wval, var);
>> + }
>> +
>> + /* SW addition: do EMC refresh adjustment here. */
>> + set_over_temp_timing(emc, next_timing, dram_over_temp_state);
>> +
>> + if (dram_type == DRAM_TYPE_LPDDR4) {
>> + mrw_req = (23 << EMC_MRW_MRW_MA_SHIFT) |
>> + (next_timing->run_clocks & EMC_MRW_MRW_OP_MASK);
>> + emc_writel(emc, mrw_req, EMC_MRW);
>> + }
>> +
>> + /* Per channel burst registers. */
>> + emc_cc_dbg(SUB_STEPS, "Writing burst_regs_per_ch\n");
>> + for (i = 0; i < next_timing->num_burst_per_ch; i++) {
>> + if (!burst_regs_per_ch_off[i])
>> + continue;
>> +
>> + if (dram_type != DRAM_TYPE_LPDDR4 &&
>> + (burst_regs_per_ch_off[i] == EMC_MRW6 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW7 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW8 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW9 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW10 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW11 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW12 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW13 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW14 ||
>> + burst_regs_per_ch_off[i] == EMC_MRW15))
>> + continue;
>> +
>> + /* Filter out second channel if not in DUAL_CHANNEL mode. */
>> + if (channel_mode != DUAL_CHANNEL &&
>> + burst_regs_per_ch_type[i] >= REG_EMC1)
>> + continue;
>> +
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->burst_reg_per_ch[i],
>> + burst_regs_per_ch_off[i]);
>> + emc_writel_per_ch(emc, next_timing->burst_reg_per_ch[i],
>> + burst_regs_per_ch_type[i],
>> + burst_regs_per_ch_off[i]);
>> + }
>> +
>> + /* Vref regs. */
>> + emc_cc_dbg(SUB_STEPS, "Writing vref_regs\n");
>> + for (i = 0; i < next_timing->vref_num; i++) {
>> + if (!vref_regs_per_ch_off[i])
>> + continue;
>> +
>> + if (channel_mode != DUAL_CHANNEL &&
>> + vref_regs_per_ch_type[i] >= REG_EMC1)
>> + continue;
>> +
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->vref_perch_regs[i],
>> + vref_regs_per_ch_off[i]);
>> + emc_writel_per_ch(emc, next_timing->vref_perch_regs[i],
>> + vref_regs_per_ch_type[i],
>> + vref_regs_per_ch_off[i]);
>> + }
>> +
>> + /* Trimmers. */
>> + emc_cc_dbg(SUB_STEPS, "Writing trim_regs\n");
>> + for (i = 0; i < next_timing->num_trim; i++) {
>> + u64 trim_reg;
>> +
>> + if (!trim_regs_off[i])
>> + continue;
>> +
>> + trim_reg = trim_regs_off[i];
>> + if (compensate_trimmer_applicable &&
>> + (trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 ||
>> + trim_reg == EMC_DATA_BRLSHFT_0 ||
>> + trim_reg == EMC_DATA_BRLSHFT_1)) {
>> + u32 reg = tegra210_apply_periodic_compensation_trimmer(
>> + next_timing, trim_reg);
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, reg,
>> + trim_regs_off[i]);
>> + emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
>> + (u32)(u64)trim_regs_off[i], reg);
>> + emc_writel(emc, reg, trim_regs_off[i]);
>> + } else {
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->trim_regs[i],
>> + trim_regs_off[i]);
>> + emc_writel(emc, next_timing->trim_regs[i],
>> + trim_regs_off[i]);
>> + }
>> + }
>> +
>> + /* Per channel trimmers. */
>> + emc_cc_dbg(SUB_STEPS, "Writing trim_regs_per_ch\n");
>> + for (i = 0; i < next_timing->num_trim_per_ch; i++) {
>> + u32 trim_reg;
>> +
>> + if (!trim_regs_per_ch_off[i])
>> + continue;
>> +
>> + if (channel_mode != DUAL_CHANNEL &&
>> + trim_regs_per_ch_type[i] >= REG_EMC1)
>> + continue;
>> +
>> + trim_reg = trim_regs_per_ch_off[i];
>> + if (compensate_trimmer_applicable &&
>> + (trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 ||
>> + trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 ||
>> + trim_reg == EMC_DATA_BRLSHFT_0 ||
>> + trim_reg == EMC_DATA_BRLSHFT_1)) {
>> + u32 reg =
>> + tegra210_apply_periodic_compensation_trimmer(
>> + next_timing, trim_reg);
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, reg, trim_regs_per_ch_off[i]);
>> + emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
>> + trim_regs_per_ch_off[i], reg);
>> + emc_writel_per_ch(emc, reg, trim_regs_per_ch_type[i],
>> + trim_regs_per_ch_off[i]);
>> + } else {
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->trim_perch_regs[i],
>> + trim_regs_per_ch_off[i]);
>> + emc_writel_per_ch(emc, next_timing->trim_perch_regs[i],
>> + trim_regs_per_ch_type[i],
>> + trim_regs_per_ch_off[i]);
>> + }
>> + }
>> +
>> + emc_cc_dbg(SUB_STEPS, "Writing burst_mc_regs\n");
>> + for (i = 0; i < next_timing->num_mc_regs; i++) {
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->burst_mc_regs[i],
>> + burst_mc_regs_off[i]);
>> + mc_writel(emc->mc, next_timing->burst_mc_regs[i],
>> + burst_mc_regs_off[i]);
>> + }
>> +
>> + /* Registers to be programmed on the faster clock. */
>> + if (next_timing->rate < last_timing->rate) {
>> + emc_cc_dbg(SUB_STEPS, "Writing la_scale_regs\n");
>> + for (i = 0; i < next_timing->num_up_down; i++) {
>> + emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
>> + i, next_timing->la_scale_regs[i],
>> + la_scale_regs_off[i]);
>> + mc_writel(emc->mc, next_timing->la_scale_regs[i],
>> + la_scale_regs_off[i]);
>> + }
>> + }
>> +
>> + /* Flush all the burst register writes. */
>> + wmb();
>
> Won't it be a bit more optimal to just read back the lastly written register rather than to flush all of memory writes?
>
Yes, should be fine. I'll give it a try.
Thanks,
Joseph
next prev parent reply other threads:[~2019-04-02 14:49 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-03-25 7:45 [PATCH 0/8] Add EMC scaling support for Tegra210 Joseph Lo
2019-03-25 7:45 ` [PATCH 1/8] dt-bindings: memory: tegra: Add Tegra210 EMC bindings Joseph Lo
2019-03-31 6:41 ` Rob Herring
2019-04-01 7:57 ` Joseph Lo
2019-04-03 4:26 ` Rob Herring
2019-04-10 2:41 ` Joseph Lo
2019-04-01 12:12 ` Dmitry Osipenko
2019-04-02 2:26 ` Joseph Lo
2019-04-02 10:21 ` Dmitry Osipenko
2019-04-04 9:17 ` Dmitry Osipenko
2019-04-04 9:30 ` Dmitry Osipenko
2019-04-08 8:49 ` Joseph Lo
2019-03-25 7:45 ` [PATCH 2/8] clk: tegra: clock changes for emc scaling support on Tegra210 Joseph Lo
2019-04-03 9:22 ` Thierry Reding
2019-04-08 7:52 ` Joseph Lo
2019-04-08 9:15 ` Peter De Schrijver
2019-03-25 7:45 ` [PATCH 3/8] memory: tegra: Add Tegra210 EMC clock driver Joseph Lo
2019-04-03 11:34 ` Thierry Reding
2019-04-08 9:25 ` Peter De Schrijver
2019-04-03 11:55 ` Dmitry Osipenko
2019-03-25 7:45 ` [PATCH 4/8] memory: tegra: add EMC scaling support code for Tegra210 Joseph Lo
2019-04-02 11:39 ` Dmitry Osipenko
2019-04-02 14:53 ` Joseph Lo
2019-03-25 7:45 ` [PATCH 5/8] memory: tegra: Add EMC scaling sequence " Joseph Lo
2019-04-02 11:36 ` Dmitry Osipenko
2019-04-02 14:49 ` Joseph Lo [this message]
2019-03-25 7:45 ` [PATCH 6/8] arm64: tegra: Add external memory controller node " Joseph Lo
2019-03-29 14:41 ` [PATCH 0/8] Add EMC scaling support " Peter De Schrijver
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aa0be43b-f1fd-e7dc-3275-d7a73fb6f48e@nvidia.com \
--to=josephl@nvidia.com \
--cc=devicetree@vger.kernel.org \
--cc=digetx@gmail.com \
--cc=jonathanh@nvidia.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=pdeschrijver@nvidia.com \
--cc=robh+dt@kernel.org \
--cc=sboyd@kernel.org \
--cc=thierry.reding@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).