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/of_device.h>
  11#include <linux/slab.h>
  12
  13#include "bcm-voter.h"
  14#include "icc-rpmh.h"
  15
  16/**
  17 * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
  18 * @node: icc node to operate on
  19 */
  20void qcom_icc_pre_aggregate(struct icc_node *node)
  21{
  22        size_t i;
  23        struct qcom_icc_node *qn;
  24
  25        qn = node->data;
  26
  27        for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
  28                qn->sum_avg[i] = 0;
  29                qn->max_peak[i] = 0;
  30        }
  31}
  32EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
  33
  34/**
  35 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
  36 * @node: node to aggregate
  37 * @tag: tag to indicate which buckets to aggregate
  38 * @avg_bw: new bw to sum aggregate
  39 * @peak_bw: new bw to max aggregate
  40 * @agg_avg: existing aggregate avg bw val
  41 * @agg_peak: existing aggregate peak bw val
  42 */
  43int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
  44                       u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
  45{
  46        size_t i;
  47        struct qcom_icc_node *qn;
  48        struct qcom_icc_provider *qp;
  49
  50        qn = node->data;
  51        qp = to_qcom_provider(node->provider);
  52
  53        if (!tag)
  54                tag = QCOM_ICC_TAG_ALWAYS;
  55
  56        for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
  57                if (tag & BIT(i)) {
  58                        qn->sum_avg[i] += avg_bw;
  59                        qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
  60                }
  61
  62                if (node->init_avg || node->init_peak) {
  63                        qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
  64                        qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
  65                }
  66        }
  67
  68        *agg_avg += avg_bw;
  69        *agg_peak = max_t(u32, *agg_peak, peak_bw);
  70
  71        for (i = 0; i < qn->num_bcms; i++)
  72                qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
  73
  74        return 0;
  75}
  76EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
  77
  78/**
  79 * qcom_icc_set - set the constraints based on path
  80 * @src: source node for the path to set constraints on
  81 * @dst: destination node for the path to set constraints on
  82 *
  83 * Return: 0 on success, or an error code otherwise
  84 */
  85int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
  86{
  87        struct qcom_icc_provider *qp;
  88        struct icc_node *node;
  89
  90        if (!src)
  91                node = dst;
  92        else
  93                node = src;
  94
  95        qp = to_qcom_provider(node->provider);
  96
  97        qcom_icc_bcm_voter_commit(qp->voter);
  98
  99        return 0;
 100}
 101EXPORT_SYMBOL_GPL(qcom_icc_set);
 102
 103struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data)
 104{
 105        struct icc_node_data *ndata;
 106        struct icc_node *node;
 107
 108        node = of_icc_xlate_onecell(spec, data);
 109        if (IS_ERR(node))
 110                return ERR_CAST(node);
 111
 112        ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
 113        if (!ndata)
 114                return ERR_PTR(-ENOMEM);
 115
 116        ndata->node = node;
 117
 118        if (spec->args_count == 2)
 119                ndata->tag = spec->args[1];
 120
 121        if (spec->args_count > 2)
 122                pr_warn("%pOF: Too many arguments, path tag is not parsed\n", spec->np);
 123
 124        return ndata;
 125}
 126EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended);
 127
 128/**
 129 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
 130 * @bcm: bcm to be initialized
 131 * @dev: associated provider device
 132 *
 133 * Return: 0 on success, or an error code otherwise
 134 */
 135int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
 136{
 137        struct qcom_icc_node *qn;
 138        const struct bcm_db *data;
 139        size_t data_count;
 140        int i;
 141
 142        /* BCM is already initialised*/
 143        if (bcm->addr)
 144                return 0;
 145
 146        bcm->addr = cmd_db_read_addr(bcm->name);
 147        if (!bcm->addr) {
 148                dev_err(dev, "%s could not find RPMh address\n",
 149                        bcm->name);
 150                return -EINVAL;
 151        }
 152
 153        data = cmd_db_read_aux_data(bcm->name, &data_count);
 154        if (IS_ERR(data)) {
 155                dev_err(dev, "%s command db read error (%ld)\n",
 156                        bcm->name, PTR_ERR(data));
 157                return PTR_ERR(data);
 158        }
 159        if (!data_count) {
 160                dev_err(dev, "%s command db missing or partial aux data\n",
 161                        bcm->name);
 162                return -EINVAL;
 163        }
 164
 165        bcm->aux_data.unit = le32_to_cpu(data->unit);
 166        bcm->aux_data.width = le16_to_cpu(data->width);
 167        bcm->aux_data.vcd = data->vcd;
 168        bcm->aux_data.reserved = data->reserved;
 169        INIT_LIST_HEAD(&bcm->list);
 170        INIT_LIST_HEAD(&bcm->ws_list);
 171
 172        if (!bcm->vote_scale)
 173                bcm->vote_scale = 1000;
 174
 175        /* Link Qnodes to their respective BCMs */
 176        for (i = 0; i < bcm->num_nodes; i++) {
 177                qn = bcm->nodes[i];
 178                qn->bcms[qn->num_bcms] = bcm;
 179                qn->num_bcms++;
 180        }
 181
 182        return 0;
 183}
 184EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
 185
 186int qcom_icc_rpmh_probe(struct platform_device *pdev)
 187{
 188        const struct qcom_icc_desc *desc;
 189        struct device *dev = &pdev->dev;
 190        struct icc_onecell_data *data;
 191        struct icc_provider *provider;
 192        struct qcom_icc_node **qnodes, *qn;
 193        struct qcom_icc_provider *qp;
 194        struct icc_node *node;
 195        size_t num_nodes, i, j;
 196        int ret;
 197
 198        desc = of_device_get_match_data(dev);
 199        if (!desc)
 200                return -EINVAL;
 201
 202        qnodes = desc->nodes;
 203        num_nodes = desc->num_nodes;
 204
 205        qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
 206        if (!qp)
 207                return -ENOMEM;
 208
 209        data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), GFP_KERNEL);
 210        if (!data)
 211                return -ENOMEM;
 212
 213        provider = &qp->provider;
 214        provider->dev = dev;
 215        provider->set = qcom_icc_set;
 216        provider->pre_aggregate = qcom_icc_pre_aggregate;
 217        provider->aggregate = qcom_icc_aggregate;
 218        provider->xlate_extended = qcom_icc_xlate_extended;
 219        INIT_LIST_HEAD(&provider->nodes);
 220        provider->data = data;
 221
 222        qp->dev = dev;
 223        qp->bcms = desc->bcms;
 224        qp->num_bcms = desc->num_bcms;
 225
 226        qp->voter = of_bcm_voter_get(qp->dev, NULL);
 227        if (IS_ERR(qp->voter))
 228                return PTR_ERR(qp->voter);
 229
 230        ret = icc_provider_add(provider);
 231        if (ret)
 232                return ret;
 233
 234        for (i = 0; i < qp->num_bcms; i++)
 235                qcom_icc_bcm_init(qp->bcms[i], dev);
 236
 237        for (i = 0; i < num_nodes; i++) {
 238                qn = qnodes[i];
 239                if (!qn)
 240                        continue;
 241
 242                node = icc_node_create(qn->id);
 243                if (IS_ERR(node)) {
 244                        ret = PTR_ERR(node);
 245                        goto err;
 246                }
 247
 248                node->name = qn->name;
 249                node->data = qn;
 250                icc_node_add(node, provider);
 251
 252                for (j = 0; j < qn->num_links; j++)
 253                        icc_link_create(node, qn->links[j]);
 254
 255                data->nodes[i] = node;
 256        }
 257
 258        data->num_nodes = num_nodes;
 259        platform_set_drvdata(pdev, qp);
 260
 261        return 0;
 262err:
 263        icc_nodes_remove(provider);
 264        icc_provider_del(provider);
 265        return ret;
 266}
 267EXPORT_SYMBOL_GPL(qcom_icc_rpmh_probe);
 268
 269int qcom_icc_rpmh_remove(struct platform_device *pdev)
 270{
 271        struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
 272
 273        icc_nodes_remove(&qp->provider);
 274        return icc_provider_del(&qp->provider);
 275}
 276EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove);
 277
 278MODULE_LICENSE("GPL v2");
 279