root/drivers/memory/tegra/tegra-emc-common.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");