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 */ 19 void 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 } 31 EXPORT_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 */ 42 int 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 62 *agg_avg += avg_bw; 63 *agg_peak = max_t(u32, *agg_peak, peak_bw); 64 65 for (i = 0; i < qn->num_bcms; i++) 66 qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]); 67 68 return 0; 69 } 70 EXPORT_SYMBOL_GPL(qcom_icc_aggregate); 71 72 /** 73 * qcom_icc_set - set the constraints based on path 74 * @src: source node for the path to set constraints on 75 * @dst: destination node for the path to set constraints on 76 * 77 * Return: 0 on success, or an error code otherwise 78 */ 79 int qcom_icc_set(struct icc_node *src, struct icc_node *dst) 80 { 81 struct qcom_icc_provider *qp; 82 struct qcom_icc_node *qn; 83 struct icc_node *node; 84 85 if (!src) 86 node = dst; 87 else 88 node = src; 89 90 qp = to_qcom_provider(node->provider); 91 qn = node->data; 92 93 qn->sum_avg[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->sum_avg[QCOM_ICC_BUCKET_AMC], 94 node->avg_bw); 95 qn->max_peak[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->max_peak[QCOM_ICC_BUCKET_AMC], 96 node->peak_bw); 97 98 qcom_icc_bcm_voter_commit(qp->voter); 99 100 return 0; 101 } 102 EXPORT_SYMBOL_GPL(qcom_icc_set); 103 104 struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data) 105 { 106 struct icc_node_data *ndata; 107 struct icc_node *node; 108 109 node = of_icc_xlate_onecell(spec, data); 110 if (IS_ERR(node)) 111 return ERR_CAST(node); 112 113 ndata = kzalloc(sizeof(*ndata), GFP_KERNEL); 114 if (!ndata) 115 return ERR_PTR(-ENOMEM); 116 117 ndata->node = node; 118 119 if (spec->args_count == 2) 120 ndata->tag = spec->args[1]; 121 122 if (spec->args_count > 2) 123 pr_warn("%pOF: Too many arguments, path tag is not parsed\n", spec->np); 124 125 return ndata; 126 } 127 EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended); 128 129 /** 130 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes 131 * @bcm: bcm to be initialized 132 * @dev: associated provider device 133 * 134 * Return: 0 on success, or an error code otherwise 135 */ 136 int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) 137 { 138 struct qcom_icc_node *qn; 139 const struct bcm_db *data; 140 size_t data_count; 141 int i; 142 143 /* BCM is already initialised*/ 144 if (bcm->addr) 145 return 0; 146 147 bcm->addr = cmd_db_read_addr(bcm->name); 148 if (!bcm->addr) { 149 dev_err(dev, "%s could not find RPMh address\n", 150 bcm->name); 151 return -EINVAL; 152 } 153 154 data = cmd_db_read_aux_data(bcm->name, &data_count); 155 if (IS_ERR(data)) { 156 dev_err(dev, "%s command db read error (%ld)\n", 157 bcm->name, PTR_ERR(data)); 158 return PTR_ERR(data); 159 } 160 if (!data_count) { 161 dev_err(dev, "%s command db missing or partial aux data\n", 162 bcm->name); 163 return -EINVAL; 164 } 165 166 bcm->aux_data.unit = le32_to_cpu(data->unit); 167 bcm->aux_data.width = le16_to_cpu(data->width); 168 bcm->aux_data.vcd = data->vcd; 169 bcm->aux_data.reserved = data->reserved; 170 INIT_LIST_HEAD(&bcm->list); 171 INIT_LIST_HEAD(&bcm->ws_list); 172 173 if (!bcm->vote_scale) 174 bcm->vote_scale = 1000; 175 176 /* Link Qnodes to their respective BCMs */ 177 for (i = 0; i < bcm->num_nodes; i++) { 178 qn = bcm->nodes[i]; 179 qn->bcms[qn->num_bcms] = bcm; 180 qn->num_bcms++; 181 } 182 183 return 0; 184 } 185 EXPORT_SYMBOL_GPL(qcom_icc_bcm_init); 186 187 MODULE_LICENSE("GPL v2"); 188