linux/drivers/memory/tegra/tegra124-emc.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
 *
 * Author:
 *	Mikko Perttunen <[email protected]>
 */

#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/sort.h>
#include <linux/string.h>

#include <soc/tegra/fuse.h>
#include <soc/tegra/mc.h>

#include "mc.h"

#define EMC_FBIO_CFG5
#define EMC_FBIO_CFG5_DRAM_TYPE_MASK
#define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT
#define EMC_FBIO_CFG5_DRAM_WIDTH_X64

#define EMC_INTSTATUS
#define EMC_INTSTATUS_CLKCHANGE_COMPLETE

#define EMC_CFG
#define EMC_CFG_DRAM_CLKSTOP_PD
#define EMC_CFG_DRAM_CLKSTOP_SR
#define EMC_CFG_DRAM_ACPD
#define EMC_CFG_DYN_SREF
#define EMC_CFG_PWR_MASK
#define EMC_CFG_DSR_VTTGEN_DRV_EN

#define EMC_REFCTRL
#define EMC_REFCTRL_DEV_SEL_SHIFT
#define EMC_REFCTRL_ENABLE

#define EMC_TIMING_CONTROL
#define EMC_RC
#define EMC_RFC
#define EMC_RAS
#define EMC_RP
#define EMC_R2W
#define EMC_W2R
#define EMC_R2P
#define EMC_W2P
#define EMC_RD_RCD
#define EMC_WR_RCD
#define EMC_RRD
#define EMC_REXT
#define EMC_WDV
#define EMC_QUSE
#define EMC_QRST
#define EMC_QSAFE
#define EMC_RDV
#define EMC_REFRESH
#define EMC_BURST_REFRESH_NUM
#define EMC_PDEX2WR
#define EMC_PDEX2RD
#define EMC_PCHG2PDEN
#define EMC_ACT2PDEN
#define EMC_AR2PDEN
#define EMC_RW2PDEN
#define EMC_TXSR
#define EMC_TCKE
#define EMC_TFAW
#define EMC_TRPAB
#define EMC_TCLKSTABLE
#define EMC_TCLKSTOP
#define EMC_TREFBW
#define EMC_ODT_WRITE
#define EMC_ODT_READ
#define EMC_WEXT
#define EMC_CTT
#define EMC_RFC_SLR
#define EMC_MRS_WAIT_CNT2

#define EMC_MRS_WAIT_CNT
#define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT
#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK
#define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT
#define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK

#define EMC_MRS
#define EMC_MODE_SET_DLL_RESET
#define EMC_MODE_SET_LONG_CNT
#define EMC_EMRS
#define EMC_REF
#define EMC_PRE

#define EMC_SELF_REF
#define EMC_SELF_REF_CMD_ENABLED
#define EMC_SELF_REF_DEV_SEL_SHIFT

#define EMC_MRW

#define EMC_MRR
#define EMC_MRR_MA_SHIFT
#define LPDDR2_MR4_TEMP_SHIFT

#define EMC_XM2DQSPADCTRL3
#define EMC_FBIO_SPARE

#define EMC_FBIO_CFG6
#define EMC_EMRS2
#define EMC_MRW2
#define EMC_MRW4
#define EMC_EINPUT
#define EMC_EINPUT_DURATION
#define EMC_PUTERM_EXTRA
#define EMC_TCKESR
#define EMC_TPD

#define EMC_AUTO_CAL_CONFIG
#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_START
#define EMC_AUTO_CAL_INTERVAL
#define EMC_AUTO_CAL_STATUS
#define EMC_AUTO_CAL_STATUS_ACTIVE
#define EMC_STATUS
#define EMC_STATUS_TIMING_UPDATE_STALLED

#define EMC_CFG_2
#define EMC_CFG_2_MODE_SHIFT
#define EMC_CFG_2_DIS_STP_OB_CLK_DURING_NON_WR

#define EMC_CFG_DIG_DLL
#define EMC_CFG_DIG_DLL_PERIOD
#define EMC_RDV_MASK
#define EMC_WDV_MASK
#define EMC_CTT_DURATION
#define EMC_CTT_TERM_CTRL
#define EMC_ZCAL_INTERVAL
#define EMC_ZCAL_WAIT_CNT

#define EMC_ZQ_CAL
#define EMC_ZQ_CAL_CMD
#define EMC_ZQ_CAL_LONG
#define EMC_ZQ_CAL_LONG_CMD_DEV0
#define EMC_ZQ_CAL_LONG_CMD_DEV1

#define EMC_XM2CMDPADCTRL
#define EMC_XM2DQSPADCTRL
#define EMC_XM2DQSPADCTRL2
#define EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE
#define EMC_XM2DQSPADCTRL2_VREF_ENABLE
#define EMC_XM2DQPADCTRL
#define EMC_XM2DQPADCTRL2
#define EMC_XM2CLKPADCTRL
#define EMC_XM2COMPPADCTRL
#define EMC_XM2VTTGENPADCTRL
#define EMC_XM2VTTGENPADCTRL2
#define EMC_XM2VTTGENPADCTRL3
#define EMC_XM2DQSPADCTRL4
#define EMC_DLL_XFORM_DQS0
#define EMC_DLL_XFORM_DQS1
#define EMC_DLL_XFORM_DQS2
#define EMC_DLL_XFORM_DQS3
#define EMC_DLL_XFORM_DQS4
#define EMC_DLL_XFORM_DQS5
#define EMC_DLL_XFORM_DQS6
#define EMC_DLL_XFORM_DQS7
#define EMC_DLL_XFORM_QUSE0
#define EMC_DLL_XFORM_QUSE1
#define EMC_DLL_XFORM_QUSE2
#define EMC_DLL_XFORM_QUSE3
#define EMC_DLL_XFORM_QUSE4
#define EMC_DLL_XFORM_QUSE5
#define EMC_DLL_XFORM_QUSE6
#define EMC_DLL_XFORM_QUSE7
#define EMC_DLL_XFORM_DQ0
#define EMC_DLL_XFORM_DQ1
#define EMC_DLL_XFORM_DQ2
#define EMC_DLL_XFORM_DQ3
#define EMC_DLI_TRIM_TXDQS0
#define EMC_DLI_TRIM_TXDQS1
#define EMC_DLI_TRIM_TXDQS2
#define EMC_DLI_TRIM_TXDQS3
#define EMC_DLI_TRIM_TXDQS4
#define EMC_DLI_TRIM_TXDQS5
#define EMC_DLI_TRIM_TXDQS6
#define EMC_DLI_TRIM_TXDQS7
#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE
#define EMC_SEL_DPD_CTRL
#define EMC_SEL_DPD_CTRL_DATA_SEL_DPD
#define EMC_SEL_DPD_CTRL_ODT_SEL_DPD
#define EMC_SEL_DPD_CTRL_RESET_SEL_DPD
#define EMC_SEL_DPD_CTRL_CA_SEL_DPD
#define EMC_SEL_DPD_CTRL_CLK_SEL_DPD
#define EMC_SEL_DPD_CTRL_DDR3_MASK
#define EMC_SEL_DPD_CTRL_MASK
#define EMC_PRE_REFRESH_REQ_CNT
#define EMC_DYN_SELF_REF_CONTROL
#define EMC_TXSRDLL
#define EMC_CCFIFO_ADDR
#define EMC_CCFIFO_DATA
#define EMC_CCFIFO_STATUS
#define EMC_CDB_CNTL_1
#define EMC_CDB_CNTL_2
#define EMC_XM2CLKPADCTRL2
#define EMC_AUTO_CAL_CONFIG2
#define EMC_AUTO_CAL_CONFIG3
#define EMC_IBDLY
#define EMC_DLL_XFORM_ADDR0
#define EMC_DLL_XFORM_ADDR1
#define EMC_DLL_XFORM_ADDR2
#define EMC_DSR_VTTGEN_DRV
#define EMC_TXDSRVTTGEN
#define EMC_XM2CMDPADCTRL4
#define EMC_XM2CMDPADCTRL5
#define EMC_DLL_XFORM_DQS8
#define EMC_DLL_XFORM_DQS9
#define EMC_DLL_XFORM_DQS10
#define EMC_DLL_XFORM_DQS11
#define EMC_DLL_XFORM_DQS12
#define EMC_DLL_XFORM_DQS13
#define EMC_DLL_XFORM_DQS14
#define EMC_DLL_XFORM_DQS15
#define EMC_DLL_XFORM_QUSE8
#define EMC_DLL_XFORM_QUSE9
#define EMC_DLL_XFORM_QUSE10
#define EMC_DLL_XFORM_QUSE11
#define EMC_DLL_XFORM_QUSE12
#define EMC_DLL_XFORM_QUSE13
#define EMC_DLL_XFORM_QUSE14
#define EMC_DLL_XFORM_QUSE15
#define EMC_DLL_XFORM_DQ4
#define EMC_DLL_XFORM_DQ5
#define EMC_DLL_XFORM_DQ6
#define EMC_DLL_XFORM_DQ7
#define EMC_DLI_TRIM_TXDQS8
#define EMC_DLI_TRIM_TXDQS9
#define EMC_DLI_TRIM_TXDQS10
#define EMC_DLI_TRIM_TXDQS11
#define EMC_DLI_TRIM_TXDQS12
#define EMC_DLI_TRIM_TXDQS13
#define EMC_DLI_TRIM_TXDQS14
#define EMC_DLI_TRIM_TXDQS15
#define EMC_CDB_CNTL_3
#define EMC_XM2DQSPADCTRL5
#define EMC_XM2DQSPADCTRL6
#define EMC_XM2DQPADCTRL3
#define EMC_DLL_XFORM_ADDR3
#define EMC_DLL_XFORM_ADDR4
#define EMC_DLL_XFORM_ADDR5
#define EMC_CFG_PIPE
#define EMC_QPOP
#define EMC_QUSE_WIDTH
#define EMC_PUTERM_WIDTH
#define EMC_BGBIAS_CTL0
#define EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_RX
#define EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_VTTGEN
#define EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD
#define EMC_PUTERM_ADJ

#define DRAM_DEV_SEL_ALL
#define DRAM_DEV_SEL_0
#define DRAM_DEV_SEL_1

#define EMC_CFG_POWER_FEATURES_MASK
#define EMC_REFCTRL_DEV_SEL(n)
#define EMC_DRAM_DEV_SEL(n)

/* Maximum amount of time in us. to wait for changes to become effective */
#define EMC_STATUS_UPDATE_TIMEOUT

enum emc_dram_type {};

enum emc_dll_change {};

static const unsigned long emc_burst_regs[] =;

struct emc_timing {};

enum emc_rate_request_type {};

struct emc_rate_request {};

struct tegra_emc {};

/* Timing change sequence functions */

static void emc_ccfifo_writel(struct tegra_emc *emc, u32 value,
			      unsigned long offset)
{}

static void emc_seq_update_timing(struct tegra_emc *emc)
{}

static void emc_seq_disable_auto_cal(struct tegra_emc *emc)
{}

static void emc_seq_wait_clkchange(struct tegra_emc *emc)
{}

static struct emc_timing *tegra_emc_find_timing(struct tegra_emc *emc,
						unsigned long rate)
{}

static int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
					   unsigned long rate)
{}

static void tegra_emc_complete_timing_change(struct tegra_emc *emc,
					     unsigned long rate)
{}

/* Initialization and deinitialization */

static void emc_read_current_timing(struct tegra_emc *emc,
				    struct emc_timing *timing)
{}

static int emc_init(struct tegra_emc *emc)
{}

static int load_one_timing_from_dt(struct tegra_emc *emc,
				   struct emc_timing *timing,
				   struct device_node *node)
{}

static int cmp_timings(const void *_a, const void *_b)
{}

static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
					  struct device_node *node)
{}

static const struct of_device_id tegra_emc_of_match[] =;
MODULE_DEVICE_TABLE(of, tegra_emc_of_match);

static struct device_node *
tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
{}

static void tegra_emc_rate_requests_init(struct tegra_emc *emc)
{}

static int emc_request_rate(struct tegra_emc *emc,
			    unsigned long new_min_rate,
			    unsigned long new_max_rate,
			    enum emc_rate_request_type type)
{}

static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
			    enum emc_rate_request_type type)
{}

static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
			    enum emc_rate_request_type type)
{}

/*
 * debugfs interface
 *
 * The memory controller driver exposes some files in debugfs that can be used
 * to control the EMC frequency. The top-level directory can be found here:
 *
 *   /sys/kernel/debug/emc
 *
 * It contains the following files:
 *
 *   - available_rates: This file contains a list of valid, space-separated
 *     EMC frequencies.
 *
 *   - min_rate: Writing a value to this file sets the given frequency as the
 *       floor of the permitted range. If this is higher than the currently
 *       configured EMC frequency, this will cause the frequency to be
 *       increased so that it stays within the valid range.
 *
 *   - max_rate: Similarily to the min_rate file, writing a value to this file
 *       sets the given frequency as the ceiling of the permitted range. If
 *       the value is lower than the currently configured EMC frequency, this
 *       will cause the frequency to be decreased so that it stays within the
 *       valid range.
 */

static bool tegra_emc_validate_rate(struct tegra_emc *emc, unsigned long rate)
{}

static int tegra_emc_debug_available_rates_show(struct seq_file *s,
						void *data)
{}

DEFINE_SHOW_ATTRIBUTE();

static int tegra_emc_debug_min_rate_get(void *data, u64 *rate)
{}

static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
{}

DEFINE_DEBUGFS_ATTRIBUTE();

static int tegra_emc_debug_max_rate_get(void *data, u64 *rate)
{}

static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
{}

DEFINE_DEBUGFS_ATTRIBUTE();

static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
{}

static inline struct tegra_emc *
to_tegra_emc_provider(struct icc_provider *provider)
{}

static struct icc_node_data *
emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
{}

static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
{}

static int tegra_emc_interconnect_init(struct tegra_emc *emc)
{}

static int tegra_emc_opp_table_init(struct tegra_emc *emc)
{}

static void devm_tegra_emc_unset_callback(void *data)
{}

static int tegra_emc_probe(struct platform_device *pdev)
{
	struct device_node *np;
	struct tegra_emc *emc;
	u32 ram_code;
	int err;

	emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
	if (!emc)
		return -ENOMEM;

	mutex_init(&emc->rate_lock);
	emc->dev = &pdev->dev;

	emc->regs = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(emc->regs))
		return PTR_ERR(emc->regs);

	emc->mc = devm_tegra_memory_controller_get(&pdev->dev);
	if (IS_ERR(emc->mc))
		return PTR_ERR(emc->mc);

	ram_code = tegra_read_ram_code();

	np = tegra_emc_find_node_by_ram_code(pdev->dev.of_node, ram_code);
	if (np) {
		err = tegra_emc_load_timings_from_dt(emc, np);
		of_node_put(np);
		if (err)
			return err;
	} else {
		dev_info_once(&pdev->dev,
			      "no memory timings for RAM code %u found in DT\n",
			      ram_code);
	}

	err = emc_init(emc);
	if (err) {
		dev_err(&pdev->dev, "EMC initialization failed: %d\n", err);
		return err;
	}

	platform_set_drvdata(pdev, emc);

	tegra124_clk_set_emc_callbacks(tegra_emc_prepare_timing_change,
				       tegra_emc_complete_timing_change);

	err = devm_add_action_or_reset(&pdev->dev, devm_tegra_emc_unset_callback,
				       NULL);
	if (err)
		return err;

	emc->clk = devm_clk_get(&pdev->dev, "emc");
	if (IS_ERR(emc->clk)) {
		err = PTR_ERR(emc->clk);
		dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err);
		return err;
	}

	err = tegra_emc_opp_table_init(emc);
	if (err)
		return err;

	tegra_emc_rate_requests_init(emc);

	if (IS_ENABLED(CONFIG_DEBUG_FS))
		emc_debugfs_init(&pdev->dev, emc);

	tegra_emc_interconnect_init(emc);

	/*
	 * Don't allow the kernel module to be unloaded. Unloading adds some
	 * extra complexity which doesn't really worth the effort in a case of
	 * this driver.
	 */
	try_module_get(THIS_MODULE);

	return 0;
};

static struct platform_driver tegra_emc_driver =;
module_platform_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();