1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
7 #include <linux/bitfield.h>
9 #include <linux/interconnect.h>
10 #include <linux/interconnect-provider.h>
11 #include <linux/module.h>
13 #include <linux/of_platform.h>
14 #include <linux/slab.h>
16 #include "bcm-voter.h"
17 #include "icc-common.h"
21 #define QOSGEN_MAINCTL_LO(p, qp) (0x8 + (p->port_offsets[qp]))
22 #define QOS_SLV_URG_MSG_EN_MASK GENMASK(3, 3)
23 #define QOS_DFLT_PRIO_MASK GENMASK(6, 4)
24 #define QOS_DISABLE_MASK GENMASK(24, 24)
27 * qcom_icc_set_qos - initialize static QoS configurations
28 * @qp: qcom icc provider to which @node belongs
29 * @node: qcom icc node to operate on
31 static void qcom_icc_set_qos(struct qcom_icc_provider
*qp
,
32 struct qcom_icc_node
*node
)
34 const struct qcom_icc_qosbox
*qos
= node
->qosbox
;
37 for (port
= 0; port
< qos
->num_ports
; port
++) {
38 regmap_update_bits(qp
->regmap
, QOSGEN_MAINCTL_LO(qos
, port
),
40 FIELD_PREP(QOS_DISABLE_MASK
, qos
->prio_fwd_disable
));
42 regmap_update_bits(qp
->regmap
, QOSGEN_MAINCTL_LO(qos
, port
),
44 FIELD_PREP(QOS_DFLT_PRIO_MASK
, qos
->prio
));
46 regmap_update_bits(qp
->regmap
, QOSGEN_MAINCTL_LO(qos
, port
),
47 QOS_SLV_URG_MSG_EN_MASK
,
48 FIELD_PREP(QOS_SLV_URG_MSG_EN_MASK
, qos
->urg_fwd
));
53 * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
54 * @node: icc node to operate on
56 void qcom_icc_pre_aggregate(struct icc_node
*node
)
59 struct qcom_icc_node
*qn
;
60 struct qcom_icc_provider
*qp
;
63 qp
= to_qcom_provider(node
->provider
);
65 for (i
= 0; i
< QCOM_ICC_NUM_BUCKETS
; i
++) {
70 for (i
= 0; i
< qn
->num_bcms
; i
++)
71 qcom_icc_bcm_voter_add(qp
->voter
, qn
->bcms
[i
]);
73 EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate
);
76 * qcom_icc_aggregate - aggregate bw for buckets indicated by tag
77 * @node: node to aggregate
78 * @tag: tag to indicate which buckets to aggregate
79 * @avg_bw: new bw to sum aggregate
80 * @peak_bw: new bw to max aggregate
81 * @agg_avg: existing aggregate avg bw val
82 * @agg_peak: existing aggregate peak bw val
84 int qcom_icc_aggregate(struct icc_node
*node
, u32 tag
, u32 avg_bw
,
85 u32 peak_bw
, u32
*agg_avg
, u32
*agg_peak
)
88 struct qcom_icc_node
*qn
;
93 tag
= QCOM_ICC_TAG_ALWAYS
;
95 for (i
= 0; i
< QCOM_ICC_NUM_BUCKETS
; i
++) {
97 qn
->sum_avg
[i
] += avg_bw
;
98 qn
->max_peak
[i
] = max_t(u32
, qn
->max_peak
[i
], peak_bw
);
101 if (node
->init_avg
|| node
->init_peak
) {
102 qn
->sum_avg
[i
] = max_t(u64
, qn
->sum_avg
[i
], node
->init_avg
);
103 qn
->max_peak
[i
] = max_t(u64
, qn
->max_peak
[i
], node
->init_peak
);
108 *agg_peak
= max_t(u32
, *agg_peak
, peak_bw
);
112 EXPORT_SYMBOL_GPL(qcom_icc_aggregate
);
115 * qcom_icc_set - set the constraints based on path
116 * @src: source node for the path to set constraints on
117 * @dst: destination node for the path to set constraints on
119 * Return: 0 on success, or an error code otherwise
121 int qcom_icc_set(struct icc_node
*src
, struct icc_node
*dst
)
123 struct qcom_icc_provider
*qp
;
124 struct icc_node
*node
;
131 qp
= to_qcom_provider(node
->provider
);
133 qcom_icc_bcm_voter_commit(qp
->voter
);
137 EXPORT_SYMBOL_GPL(qcom_icc_set
);
140 * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
141 * @bcm: bcm to be initialized
142 * @dev: associated provider device
144 * Return: 0 on success, or an error code otherwise
146 int qcom_icc_bcm_init(struct qcom_icc_bcm
*bcm
, struct device
*dev
)
148 struct qcom_icc_node
*qn
;
149 const struct bcm_db
*data
;
153 /* BCM is already initialised*/
157 bcm
->addr
= cmd_db_read_addr(bcm
->name
);
159 dev_err(dev
, "%s could not find RPMh address\n",
164 data
= cmd_db_read_aux_data(bcm
->name
, &data_count
);
166 dev_err(dev
, "%s command db read error (%ld)\n",
167 bcm
->name
, PTR_ERR(data
));
168 return PTR_ERR(data
);
171 dev_err(dev
, "%s command db missing or partial aux data\n",
176 bcm
->aux_data
.unit
= le32_to_cpu(data
->unit
);
177 bcm
->aux_data
.width
= le16_to_cpu(data
->width
);
178 bcm
->aux_data
.vcd
= data
->vcd
;
179 bcm
->aux_data
.reserved
= data
->reserved
;
180 INIT_LIST_HEAD(&bcm
->list
);
181 INIT_LIST_HEAD(&bcm
->ws_list
);
183 if (!bcm
->vote_scale
)
184 bcm
->vote_scale
= 1000;
186 /* Link Qnodes to their respective BCMs */
187 for (i
= 0; i
< bcm
->num_nodes
; i
++) {
189 qn
->bcms
[qn
->num_bcms
] = bcm
;
195 EXPORT_SYMBOL_GPL(qcom_icc_bcm_init
);
198 * qcom_icc_rpmh_configure_qos - configure QoS parameters
199 * @qp: qcom icc provider associated with QoS endpoint nodes
201 * Return: 0 on success, or an error code otherwise
203 static int qcom_icc_rpmh_configure_qos(struct qcom_icc_provider
*qp
)
205 struct qcom_icc_node
*qnode
;
209 ret
= clk_bulk_prepare_enable(qp
->num_clks
, qp
->clks
);
213 for (i
= 0; i
< qp
->num_nodes
; i
++) {
214 qnode
= qp
->nodes
[i
];
219 qcom_icc_set_qos(qp
, qnode
);
222 clk_bulk_disable_unprepare(qp
->num_clks
, qp
->clks
);
227 int qcom_icc_rpmh_probe(struct platform_device
*pdev
)
229 const struct qcom_icc_desc
*desc
;
230 struct device
*dev
= &pdev
->dev
;
231 struct icc_onecell_data
*data
;
232 struct icc_provider
*provider
;
233 struct qcom_icc_node
* const *qnodes
, *qn
;
234 struct qcom_icc_provider
*qp
;
235 struct icc_node
*node
;
236 size_t num_nodes
, i
, j
;
239 desc
= of_device_get_match_data(dev
);
243 qnodes
= desc
->nodes
;
244 num_nodes
= desc
->num_nodes
;
246 qp
= devm_kzalloc(dev
, sizeof(*qp
), GFP_KERNEL
);
250 data
= devm_kzalloc(dev
, struct_size(data
, nodes
, num_nodes
), GFP_KERNEL
);
253 data
->num_nodes
= num_nodes
;
255 provider
= &qp
->provider
;
257 provider
->set
= qcom_icc_set
;
258 provider
->pre_aggregate
= qcom_icc_pre_aggregate
;
259 provider
->aggregate
= qcom_icc_aggregate
;
260 provider
->xlate_extended
= qcom_icc_xlate_extended
;
261 provider
->data
= data
;
263 icc_provider_init(provider
);
266 qp
->bcms
= desc
->bcms
;
267 qp
->nodes
= desc
->nodes
;
268 qp
->num_bcms
= desc
->num_bcms
;
269 qp
->num_nodes
= desc
->num_nodes
;
271 qp
->voter
= of_bcm_voter_get(qp
->dev
, NULL
);
272 if (IS_ERR(qp
->voter
))
273 return PTR_ERR(qp
->voter
);
275 for (i
= 0; i
< qp
->num_bcms
; i
++)
276 qcom_icc_bcm_init(qp
->bcms
[i
], dev
);
278 for (i
= 0; i
< num_nodes
; i
++) {
283 node
= icc_node_create(qn
->id
);
286 goto err_remove_nodes
;
289 node
->name
= qn
->name
;
291 icc_node_add(node
, provider
);
293 for (j
= 0; j
< qn
->num_links
; j
++)
294 icc_link_create(node
, qn
->links
[j
]);
296 data
->nodes
[i
] = node
;
300 struct resource
*res
;
303 base
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
305 goto skip_qos_config
;
307 qp
->regmap
= devm_regmap_init_mmio(dev
, base
, desc
->config
);
308 if (IS_ERR(qp
->regmap
)) {
309 dev_info(dev
, "Skipping QoS, regmap failed; %ld\n", PTR_ERR(qp
->regmap
));
310 goto skip_qos_config
;
313 qp
->num_clks
= devm_clk_bulk_get_all(qp
->dev
, &qp
->clks
);
314 if (qp
->num_clks
== -EPROBE_DEFER
)
315 return dev_err_probe(dev
, qp
->num_clks
, "Failed to get QoS clocks\n");
317 if (qp
->num_clks
< 0 || (!qp
->num_clks
&& desc
->qos_requires_clocks
)) {
318 dev_info(dev
, "Skipping QoS, failed to get clk: %d\n", qp
->num_clks
);
319 goto skip_qos_config
;
322 ret
= qcom_icc_rpmh_configure_qos(qp
);
324 dev_info(dev
, "Failed to program QoS: %d\n", ret
);
328 ret
= icc_provider_register(provider
);
330 goto err_remove_nodes
;
332 platform_set_drvdata(pdev
, qp
);
334 /* Populate child NoC devices if any */
335 if (of_get_child_count(dev
->of_node
) > 0) {
336 ret
= of_platform_populate(dev
->of_node
, NULL
, NULL
, dev
);
338 goto err_deregister_provider
;
343 err_deregister_provider
:
344 icc_provider_deregister(provider
);
346 icc_nodes_remove(provider
);
350 EXPORT_SYMBOL_GPL(qcom_icc_rpmh_probe
);
352 void qcom_icc_rpmh_remove(struct platform_device
*pdev
)
354 struct qcom_icc_provider
*qp
= platform_get_drvdata(pdev
);
356 icc_provider_deregister(&qp
->provider
);
357 icc_nodes_remove(&qp
->provider
);
359 EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove
);
361 MODULE_DESCRIPTION("Qualcomm RPMh interconnect driver");
362 MODULE_LICENSE("GPL v2");