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