// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #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");