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