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 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 } 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