mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
As is, the EMC drivers for each 32-bit platform contain almost identical duplicated code for aggregating rate requests. Move this code out to a shared tegra-emc-common file to reduce duplication, and add kerneldoc comments. Based on code from the tegra20-emc driver. Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Link: https://patch.msgid.link/20260501-memory-refactor-v3-1-69fb1ae1a7ca@nvidia.com Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
144 lines
4.0 KiB
C
144 lines
4.0 KiB
C
// 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");
|