| // 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"); |