select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ
select DDR
+ select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra20 chips. The EMC controls the external DRAM on the board.
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
select PM_OPP
select DDR
+ select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra30 chips. The EMC controls the external DRAM on the board.
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
select TEGRA124_CLK_EMC if ARCH_TEGRA
select PM_OPP
+ select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra124 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
+config TEGRA_EMC_COMMON
+ tristate
+
endif
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
+obj-$(CONFIG_TEGRA_EMC_COMMON) += tegra-emc-common.o
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_opp.h>
+
+#include "tegra-emc-common.h"
+
+/**
+ * tegra_emc_rate_requests_init() - Initialize EMC rate request tracking
+ * @reqs: struct tegra_emc_rate_requests to initialize.
+ * @dev: EMC device.
+ *
+ * Initializes the rate request tracking state with default state
+ * (no active requests). Must be called before using @reqs with
+ * other functions.
+ */
+void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
+ struct device *dev)
+{
+ unsigned int i;
+
+ mutex_init(&reqs->rate_lock);
+ reqs->dev = dev;
+
+ for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++) {
+ reqs->requested_rate[i].min_rate = 0;
+ reqs->requested_rate[i].max_rate = ULONG_MAX;
+ }
+}
+EXPORT_SYMBOL_GPL(tegra_emc_rate_requests_init);
+
+/* Caller must hold reqs->rate_lock. */
+static int tegra_emc_request_rate(struct tegra_emc_rate_requests *reqs,
+ unsigned long new_min_rate,
+ unsigned long new_max_rate,
+ enum tegra_emc_rate_request_type type)
+{
+ struct tegra_emc_rate_request *req = reqs->requested_rate;
+ unsigned long min_rate = 0, max_rate = ULONG_MAX;
+ unsigned int i;
+ int err;
+
+ lockdep_assert_held(&reqs->rate_lock);
+
+ /* select minimum and maximum rates among the requested rates */
+ for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++, req++) {
+ if (i == type) {
+ min_rate = max(new_min_rate, min_rate);
+ max_rate = min(new_max_rate, max_rate);
+ } else {
+ min_rate = max(req->min_rate, min_rate);
+ max_rate = min(req->max_rate, max_rate);
+ }
+ }
+
+ if (min_rate > max_rate) {
+ dev_err_ratelimited(reqs->dev, "%s: type %u: out of range: %lu %lu\n",
+ __func__, type, min_rate, max_rate);
+ return -ERANGE;
+ }
+
+ /*
+ * EMC rate-changes should go via OPP API because it manages voltage
+ * changes.
+ */
+ err = dev_pm_opp_set_rate(reqs->dev, min_rate);
+ if (err)
+ return err;
+
+ reqs->requested_rate[type].min_rate = new_min_rate;
+ reqs->requested_rate[type].max_rate = new_max_rate;
+
+ return 0;
+}
+
+/**
+ * tegra_emc_set_min_rate() - Update minimum rate request for a request type
+ * @reqs: rate request tracking state
+ * @rate: new minimum rate in Hz requested by @type
+ * @type: type of request
+ *
+ * Records @rate as the new minimum rate request for @type, recalculates target
+ * rate based on all requests and applies new rate through the OPP API.
+ *
+ * Context: Sleeps. Requests to same @reqs are synchronized via mutex.
+ *
+ * Return:
+ * * %0 - success
+ * * %-ERANGE - request would cause minimum rate request to be higher than
+ * maximum rate request
+ * * other - setting new rate failed
+ */
+int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
+ unsigned long rate,
+ enum tegra_emc_rate_request_type type)
+{
+ struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
+ int ret;
+
+ mutex_lock(&reqs->rate_lock);
+ ret = tegra_emc_request_rate(reqs, rate, req->max_rate, type);
+ mutex_unlock(&reqs->rate_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_emc_set_min_rate);
+
+/**
+ * tegra_emc_set_max_rate() - Update maximum rate request for a request type
+ * @reqs: rate request tracking state
+ * @rate: new maximum rate in Hz requested by @type
+ * @type: type of request
+ *
+ * Records @rate as the new maximum rate request for @type, recalculates target
+ * rate based on all requests and applies new rate through the OPP API.
+ *
+ * Context: Sleeps. Requests to same @reqs are synchronized via mutex.
+ *
+ * Return:
+ * * %0 - success
+ * * %-ERANGE - request would cause minimum rate request to be higher than
+ * maximum rate request
+ * * other - setting new rate failed
+ */
+int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
+ unsigned long rate,
+ enum tegra_emc_rate_request_type type)
+{
+ struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
+ int ret;
+
+ mutex_lock(&reqs->rate_lock);
+ ret = tegra_emc_request_rate(reqs, req->min_rate, rate, type);
+ mutex_unlock(&reqs->rate_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_emc_set_max_rate);
+
+MODULE_DESCRIPTION("NVIDIA Tegra EMC common code");
+MODULE_LICENSE("GPL");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef TEGRA_EMC_COMMON_H
+#define TEGRA_EMC_COMMON_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+/**
+ * enum tegra_emc_rate_request_type - source of rate request
+ * @TEGRA_EMC_RATE_DEVFREQ: rate requested by devfreq governor
+ * @TEGRA_EMC_RATE_DEBUG: rate requested through debugfs knobs
+ * @TEGRA_EMC_RATE_ICC: rate requested by ICC framework
+ * @TEGRA_EMC_RATE_TYPE_MAX: number of valid request types
+ */
+enum tegra_emc_rate_request_type {
+ TEGRA_EMC_RATE_DEVFREQ,
+ TEGRA_EMC_RATE_DEBUG,
+ TEGRA_EMC_RATE_ICC,
+ TEGRA_EMC_RATE_TYPE_MAX,
+};
+
+struct tegra_emc_rate_request {
+ unsigned long min_rate;
+ unsigned long max_rate;
+};
+
+struct tegra_emc_rate_requests {
+ struct tegra_emc_rate_request requested_rate[TEGRA_EMC_RATE_TYPE_MAX];
+ /* Protects @requested_rate. */
+ struct mutex rate_lock;
+ struct device *dev;
+};
+
+void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
+ struct device *dev);
+
+int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
+ unsigned long rate,
+ enum tegra_emc_rate_request_type type);
+
+int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
+ unsigned long rate,
+ enum tegra_emc_rate_request_type type);
+
+#endif /* TEGRA_EMC_COMMON_H */
#include <soc/tegra/mc.h>
#include "mc.h"
+#include "tegra-emc-common.h"
#define EMC_FBIO_CFG5 0x104
#define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3
u32 emc_zcal_interval;
};
-enum emc_rate_request_type {
- EMC_RATE_DEBUG,
- EMC_RATE_ICC,
- EMC_RATE_TYPE_MAX,
-};
-
-struct emc_rate_request {
- unsigned long min_rate;
- unsigned long max_rate;
-};
-
struct tegra_emc {
struct device *dev;
struct icc_provider provider;
- /*
- * There are multiple sources in the EMC driver which could request
- * a min/max clock rate, these rates are contained in this array.
- */
- struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
-
- /* protect shared rate-change code path */
- struct mutex rate_lock;
+ struct tegra_emc_rate_requests reqs;
};
/* Timing change sequence functions */
return NULL;
}
-static void tegra124_emc_rate_requests_init(struct tegra_emc *emc)
-{
- unsigned int i;
-
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
- emc->requested_rate[i].min_rate = 0;
- emc->requested_rate[i].max_rate = ULONG_MAX;
- }
-}
-
-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)
-{
- struct emc_rate_request *req = emc->requested_rate;
- unsigned long min_rate = 0, max_rate = ULONG_MAX;
- unsigned int i;
- int err;
-
- /* select minimum and maximum rates among the requested rates */
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
- if (i == type) {
- min_rate = max(new_min_rate, min_rate);
- max_rate = min(new_max_rate, max_rate);
- } else {
- min_rate = max(req->min_rate, min_rate);
- max_rate = min(req->max_rate, max_rate);
- }
- }
-
- if (min_rate > max_rate) {
- dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
- __func__, type, min_rate, max_rate);
- return -ERANGE;
- }
-
- /*
- * EMC rate-changes should go via OPP API because it manages voltage
- * changes.
- */
- err = dev_pm_opp_set_rate(emc->dev, min_rate);
- if (err)
- return err;
-
- emc->requested_rate[type].min_rate = new_min_rate;
- emc->requested_rate[type].max_rate = new_max_rate;
-
- return 0;
-}
-
-static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, rate, req->max_rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
-static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, req->min_rate, rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
/*
* debugfs interface
*
if (!tegra124_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
if (!tegra124_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
do_div(rate, ddr * dram_data_bus_width_bytes);
rate = min_t(u64, rate, U32_MAX);
- err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
if (err)
return err;
if (!emc)
return -ENOMEM;
- mutex_init(&emc->rate_lock);
emc->dev = &pdev->dev;
emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (err)
return err;
- tegra124_emc_rate_requests_init(emc);
+ tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
if (IS_ENABLED(CONFIG_DEBUG_FS))
emc_debugfs_init(&pdev->dev, emc);
#include "../of_memory.h"
#include "mc.h"
+#include "tegra-emc-common.h"
#define EMC_INTSTATUS 0x000
#define EMC_INTMASK 0x004
u32 data[ARRAY_SIZE(emc_timing_registers)];
};
-enum emc_rate_request_type {
- EMC_RATE_DEVFREQ,
- EMC_RATE_DEBUG,
- EMC_RATE_ICC,
- EMC_RATE_TYPE_MAX,
-};
-
-struct emc_rate_request {
- unsigned long min_rate;
- unsigned long max_rate;
-};
-
struct tegra_emc {
struct device *dev;
struct tegra_mc *mc;
unsigned long max_rate;
} debugfs;
- /*
- * There are multiple sources in the EMC driver which could request
- * a min/max clock rate, these rates are contained in this array.
- */
- struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
-
- /* protect shared rate-change code path */
- struct mutex rate_lock;
+ struct tegra_emc_rate_requests reqs;
struct devfreq_simple_ondemand_data ondemand_data;
return timing->rate;
}
-static void tegra20_emc_rate_requests_init(struct tegra_emc *emc)
-{
- unsigned int i;
-
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
- emc->requested_rate[i].min_rate = 0;
- emc->requested_rate[i].max_rate = ULONG_MAX;
- }
-}
-
-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)
-{
- struct emc_rate_request *req = emc->requested_rate;
- unsigned long min_rate = 0, max_rate = ULONG_MAX;
- unsigned int i;
- int err;
-
- /* select minimum and maximum rates among the requested rates */
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
- if (i == type) {
- min_rate = max(new_min_rate, min_rate);
- max_rate = min(new_max_rate, max_rate);
- } else {
- min_rate = max(req->min_rate, min_rate);
- max_rate = min(req->max_rate, max_rate);
- }
- }
-
- if (min_rate > max_rate) {
- dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
- __func__, type, min_rate, max_rate);
- return -ERANGE;
- }
-
- /*
- * EMC rate-changes should go via OPP API because it manages voltage
- * changes.
- */
- err = dev_pm_opp_set_rate(emc->dev, min_rate);
- if (err)
- return err;
-
- emc->requested_rate[type].min_rate = new_min_rate;
- emc->requested_rate[type].max_rate = new_max_rate;
-
- return 0;
-}
-
-static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, rate, req->max_rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
-static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, req->min_rate, rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
/*
* debugfs interface
*
if (!tegra20_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
if (!tegra20_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
do_div(rate, dram_data_bus_width_bytes);
rate = min_t(u64, rate, U32_MAX);
- err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
if (err)
return err;
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
- return emc_set_min_rate(emc, rate, EMC_RATE_DEVFREQ);
+ return tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEVFREQ);
}
static int tegra20_emc_devfreq_get_dev_status(struct device *dev,
if (!emc)
return -ENOMEM;
- mutex_init(&emc->rate_lock);
emc->clk_nb.notifier_call = tegra20_emc_clk_change_notify;
emc->dev = &pdev->dev;
return err;
platform_set_drvdata(pdev, emc);
- tegra20_emc_rate_requests_init(emc);
+ tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
tegra20_emc_debugfs_init(emc);
tegra20_emc_interconnect_init(emc);
tegra20_emc_devfreq_init(emc);
#include "../of_memory.h"
#include "mc.h"
+#include "tegra-emc-common.h"
#define EMC_INTSTATUS 0x000
#define EMC_INTMASK 0x004
bool emc_cfg_dyn_self_ref;
};
-enum emc_rate_request_type {
- EMC_RATE_DEBUG,
- EMC_RATE_ICC,
- EMC_RATE_TYPE_MAX,
-};
-
-struct emc_rate_request {
- unsigned long min_rate;
- unsigned long max_rate;
-};
-
struct tegra_emc {
struct device *dev;
struct tegra_mc *mc;
unsigned long max_rate;
} debugfs;
- /*
- * There are multiple sources in the EMC driver which could request
- * a min/max clock rate, these rates are contained in this array.
- */
- struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
-
- /* protect shared rate-change code path */
- struct mutex rate_lock;
+ struct tegra_emc_rate_requests reqs;
bool mrr_error;
};
return timing->rate;
}
-static void tegra30_emc_rate_requests_init(struct tegra_emc *emc)
-{
- unsigned int i;
-
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
- emc->requested_rate[i].min_rate = 0;
- emc->requested_rate[i].max_rate = ULONG_MAX;
- }
-}
-
-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)
-{
- struct emc_rate_request *req = emc->requested_rate;
- unsigned long min_rate = 0, max_rate = ULONG_MAX;
- unsigned int i;
- int err;
-
- /* select minimum and maximum rates among the requested rates */
- for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
- if (i == type) {
- min_rate = max(new_min_rate, min_rate);
- max_rate = min(new_max_rate, max_rate);
- } else {
- min_rate = max(req->min_rate, min_rate);
- max_rate = min(req->max_rate, max_rate);
- }
- }
-
- if (min_rate > max_rate) {
- dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
- __func__, type, min_rate, max_rate);
- return -ERANGE;
- }
-
- /*
- * EMC rate-changes should go via OPP API because it manages voltage
- * changes.
- */
- err = dev_pm_opp_set_rate(emc->dev, min_rate);
- if (err)
- return err;
-
- emc->requested_rate[type].min_rate = new_min_rate;
- emc->requested_rate[type].max_rate = new_max_rate;
-
- return 0;
-}
-
-static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, rate, req->max_rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
-static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
- enum emc_rate_request_type type)
-{
- struct emc_rate_request *req = &emc->requested_rate[type];
- int ret;
-
- mutex_lock(&emc->rate_lock);
- ret = emc_request_rate(emc, req->min_rate, rate, type);
- mutex_unlock(&emc->rate_lock);
-
- return ret;
-}
-
/*
* debugfs interface
*
if (!tegra30_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
if (!tegra30_emc_validate_rate(emc, rate))
return -EINVAL;
- err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
+ err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
if (err < 0)
return err;
do_div(rate, ddr * dram_data_bus_width_bytes);
rate = min_t(u64, rate, U32_MAX);
- err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
+ err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
if (err)
return err;
if (IS_ERR(emc->mc))
return PTR_ERR(emc->mc);
- mutex_init(&emc->rate_lock);
emc->clk_nb.notifier_call = emc_clk_change_notify;
emc->dev = &pdev->dev;
return err;
platform_set_drvdata(pdev, emc);
- tegra30_emc_rate_requests_init(emc);
+ tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
tegra30_emc_debugfs_init(emc);
tegra30_emc_interconnect_init(emc);