linux/drivers/interconnect/qcom/icc-rpmh.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/interconnect.h>
   7#include <linux/interconnect-provider.h>
   8#include <linux/module.h>
   9#include <linux/of.h>
  10#include <linux/slab.h>
  11
  12#include "bcm-voter.h"
  13#include "icc-rpmh.h"
  14
  15/**
  16 * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
  17 * @node: icc node to operate on
  18 */
  19void qcom_icc_pre_aggregate(struct icc_node *node)
  20{
  21        size_t i;
  22        struct qcom_icc_node *qn;
  23
  24        qn = node->data;
  25
  26        for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
  27                qn->sum_avg[i] = 0;
  28                qn->max_peak[i] = 0;
  29        }
  30}
  31EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
  32
  33/**
  34 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
  35 * @node: node to aggregate
  36 * @tag: tag to indicate which buckets to aggregate
  37 * @avg_bw: new bw to sum aggregate
  38 * @peak_bw: new bw to max aggregate
  39 * @agg_avg: existing aggregate avg bw val
  40 * @agg_peak: existing aggregate peak bw val
  41 */
  42int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
  43                       u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
  44{
  45        size_t i;
  46        struct qcom_icc_node *qn;
  47        struct qcom_icc_provider *qp;
  48
  49        qn = node->data;
  50        qp = to_qcom_provider(node->provider);
  51
  52        if (!tag)
  53                tag = QCOM_ICC_TAG_ALWAYS;
  54
  55        for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
  56                if (tag & BIT(i)) {
  57                        qn->sum_avg[i] += avg_bw;
  58                        qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
  59                }
  60
  61                if (node->init_avg || node->init_peak) {
  62                        qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
  63                        qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
  64                }
  65        }
  66
  67        *agg_avg += avg_bw;
  68        *agg_peak = max_t(u32, *agg_peak, peak_bw);
  69
  70        for (i = 0; i < qn->num_bcms; i++)
  71                qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
  72
  73        return 0;
  74}
  75EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
  76
  77/**
  78 * qcom_icc_set - set the constraints based on path
  79 * @src: source node for the path to set constraints on
  80 * @dst: destination node for the path to set constraints on
  81 *
  82 * Return: 0 on success, or an error code otherwise
  83 */
  84int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
  85{
  86        struct qcom_icc_provider *qp;
  87        struct icc_node *node;
  88
  89        if (!src)
  90                node = dst;
  91        else
  92                node = src;
  93
  94        qp = to_qcom_provider(node->provider);
  95
  96        qcom_icc_bcm_voter_commit(qp->voter);
  97
  98        return 0;
  99}
 100EXPORT_SYMBOL_GPL(qcom_icc_set);
 101
 102struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data)
 103{
 104        struct icc_node_data *ndata;
 105        struct icc_node *node;
 106
 107        node = of_icc_xlate_onecell(spec, data);
 108        if (IS_ERR(node))
 109                return ERR_CAST(node);
 110
 111        ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
 112        if (!ndata)
 113                return ERR_PTR(-ENOMEM);
 114
 115        ndata->node = node;
 116
 117        if (spec->args_count == 2)
 118                ndata->tag = spec->args[1];
 119
 120        if (spec->args_count > 2)
 121                pr_warn("%pOF: Too many arguments, path tag is not parsed\n", spec->np);
 122
 123        return ndata;
 124}
 125EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended);
 126
 127/**
 128 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
 129 * @bcm: bcm to be initialized
 130 * @dev: associated provider device
 131 *
 132 * Return: 0 on success, or an error code otherwise
 133 */
 134int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
 135{
 136        struct qcom_icc_node *qn;
 137        const struct bcm_db *data;
 138        size_t data_count;
 139        int i;
 140
 141        /* BCM is already initialised*/
 142        if (bcm->addr)
 143                return 0;
 144
 145        bcm->addr = cmd_db_read_addr(bcm->name);
 146        if (!bcm->addr) {
 147                dev_err(dev, "%s could not find RPMh address\n",
 148                        bcm->name);
 149                return -EINVAL;
 150        }
 151
 152        data = cmd_db_read_aux_data(bcm->name, &data_count);
 153        if (IS_ERR(data)) {
 154                dev_err(dev, "%s command db read error (%ld)\n",
 155                        bcm->name, PTR_ERR(data));
 156                return PTR_ERR(data);
 157        }
 158        if (!data_count) {
 159                dev_err(dev, "%s command db missing or partial aux data\n",
 160                        bcm->name);
 161                return -EINVAL;
 162        }
 163
 164        bcm->aux_data.unit = le32_to_cpu(data->unit);
 165        bcm->aux_data.width = le16_to_cpu(data->width);
 166        bcm->aux_data.vcd = data->vcd;
 167        bcm->aux_data.reserved = data->reserved;
 168        INIT_LIST_HEAD(&bcm->list);
 169        INIT_LIST_HEAD(&bcm->ws_list);
 170
 171        if (!bcm->vote_scale)
 172                bcm->vote_scale = 1000;
 173
 174        /* Link Qnodes to their respective BCMs */
 175        for (i = 0; i < bcm->num_nodes; i++) {
 176                qn = bcm->nodes[i];
 177                qn->bcms[qn->num_bcms] = bcm;
 178                qn->num_bcms++;
 179        }
 180
 181        return 0;
 182}
 183EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
 184
 185MODULE_LICENSE("GPL v2");
 186