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 10 #include "bcm-voter.h" 11 #include "icc-rpmh.h" 12 13 /** 14 * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set 15 * @node: icc node to operate on 16 */ 17 void qcom_icc_pre_aggregate(struct icc_node *node) 18 { 19 size_t i; 20 struct qcom_icc_node *qn; 21 22 qn = node->data; 23 24 for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { 25 qn->sum_avg[i] = 0; 26 qn->max_peak[i] = 0; 27 } 28 } 29 EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate); 30 31 /** 32 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag 33 * @node: node to aggregate 34 * @tag: tag to indicate which buckets to aggregate 35 * @avg_bw: new bw to sum aggregate 36 * @peak_bw: new bw to max aggregate 37 * @agg_avg: existing aggregate avg bw val 38 * @agg_peak: existing aggregate peak bw val 39 */ 40 int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, 41 u32 peak_bw, u32 *agg_avg, u32 *agg_peak) 42 { 43 size_t i; 44 struct qcom_icc_node *qn; 45 struct qcom_icc_provider *qp; 46 47 qn = node->data; 48 qp = to_qcom_provider(node->provider); 49 50 if (!tag) 51 tag = QCOM_ICC_TAG_ALWAYS; 52 53 for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { 54 if (tag & BIT(i)) { 55 qn->sum_avg[i] += avg_bw; 56 qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw); 57 } 58 } 59 60 *agg_avg += avg_bw; 61 *agg_peak = max_t(u32, *agg_peak, peak_bw); 62 63 for (i = 0; i < qn->num_bcms; i++) 64 qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]); 65 66 return 0; 67 } 68 EXPORT_SYMBOL_GPL(qcom_icc_aggregate); 69 70 /** 71 * qcom_icc_set - set the constraints based on path 72 * @src: source node for the path to set constraints on 73 * @dst: destination node for the path to set constraints on 74 * 75 * Return: 0 on success, or an error code otherwise 76 */ 77 int qcom_icc_set(struct icc_node *src, struct icc_node *dst) 78 { 79 struct qcom_icc_provider *qp; 80 struct icc_node *node; 81 82 if (!src) 83 node = dst; 84 else 85 node = src; 86 87 qp = to_qcom_provider(node->provider); 88 89 qcom_icc_bcm_voter_commit(qp->voter); 90 91 return 0; 92 } 93 EXPORT_SYMBOL_GPL(qcom_icc_set); 94 95 /** 96 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes 97 * @bcm: bcm to be initialized 98 * @dev: associated provider device 99 * 100 * Return: 0 on success, or an error code otherwise 101 */ 102 int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) 103 { 104 struct qcom_icc_node *qn; 105 const struct bcm_db *data; 106 size_t data_count; 107 int i; 108 109 /* BCM is already initialised*/ 110 if (bcm->addr) 111 return 0; 112 113 bcm->addr = cmd_db_read_addr(bcm->name); 114 if (!bcm->addr) { 115 dev_err(dev, "%s could not find RPMh address\n", 116 bcm->name); 117 return -EINVAL; 118 } 119 120 data = cmd_db_read_aux_data(bcm->name, &data_count); 121 if (IS_ERR(data)) { 122 dev_err(dev, "%s command db read error (%ld)\n", 123 bcm->name, PTR_ERR(data)); 124 return PTR_ERR(data); 125 } 126 if (!data_count) { 127 dev_err(dev, "%s command db missing or partial aux data\n", 128 bcm->name); 129 return -EINVAL; 130 } 131 132 bcm->aux_data.unit = le32_to_cpu(data->unit); 133 bcm->aux_data.width = le16_to_cpu(data->width); 134 bcm->aux_data.vcd = data->vcd; 135 bcm->aux_data.reserved = data->reserved; 136 INIT_LIST_HEAD(&bcm->list); 137 INIT_LIST_HEAD(&bcm->ws_list); 138 139 /* Link Qnodes to their respective BCMs */ 140 for (i = 0; i < bcm->num_nodes; i++) { 141 qn = bcm->nodes[i]; 142 qn->bcms[qn->num_bcms] = bcm; 143 qn->num_bcms++; 144 } 145 146 return 0; 147 } 148 EXPORT_SYMBOL_GPL(qcom_icc_bcm_init); 149 150 MODULE_LICENSE("GPL v2"); 151