cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

icc-rpmh.c (6342B)


      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	struct qcom_icc_provider *qp;
     25
     26	qn = node->data;
     27	qp = to_qcom_provider(node->provider);
     28
     29	for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
     30		qn->sum_avg[i] = 0;
     31		qn->max_peak[i] = 0;
     32	}
     33
     34	for (i = 0; i < qn->num_bcms; i++)
     35		qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
     36}
     37EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
     38
     39/**
     40 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
     41 * @node: node to aggregate
     42 * @tag: tag to indicate which buckets to aggregate
     43 * @avg_bw: new bw to sum aggregate
     44 * @peak_bw: new bw to max aggregate
     45 * @agg_avg: existing aggregate avg bw val
     46 * @agg_peak: existing aggregate peak bw val
     47 */
     48int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
     49		       u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
     50{
     51	size_t i;
     52	struct qcom_icc_node *qn;
     53
     54	qn = node->data;
     55
     56	if (!tag)
     57		tag = QCOM_ICC_TAG_ALWAYS;
     58
     59	for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
     60		if (tag & BIT(i)) {
     61			qn->sum_avg[i] += avg_bw;
     62			qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
     63		}
     64
     65		if (node->init_avg || node->init_peak) {
     66			qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
     67			qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
     68		}
     69	}
     70
     71	*agg_avg += avg_bw;
     72	*agg_peak = max_t(u32, *agg_peak, peak_bw);
     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 * const *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");