|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 | /*
|
3 | 3 | * Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
| 4 | + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. |
4 | 5 | */
|
5 | 6 |
|
| 7 | +#include <linux/bitfield.h> |
| 8 | +#include <linux/clk.h> |
6 | 9 | #include <linux/interconnect.h>
|
7 | 10 | #include <linux/interconnect-provider.h>
|
8 | 11 | #include <linux/module.h>
|
|
14 | 17 | #include "icc-common.h"
|
15 | 18 | #include "icc-rpmh.h"
|
16 | 19 |
|
| 20 | +/* QNOC QoS */ |
| 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) |
| 25 | + |
| 26 | +/** |
| 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 |
| 30 | + */ |
| 31 | +static void qcom_icc_set_qos(struct qcom_icc_provider *qp, |
| 32 | + struct qcom_icc_node *node) |
| 33 | +{ |
| 34 | + const struct qcom_icc_qosbox *qos = node->qosbox; |
| 35 | + int port; |
| 36 | + |
| 37 | + for (port = 0; port < qos->num_ports; port++) { |
| 38 | + regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port), |
| 39 | + QOS_DISABLE_MASK, |
| 40 | + FIELD_PREP(QOS_DISABLE_MASK, qos->prio_fwd_disable)); |
| 41 | + |
| 42 | + regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port), |
| 43 | + QOS_DFLT_PRIO_MASK, |
| 44 | + FIELD_PREP(QOS_DFLT_PRIO_MASK, qos->prio)); |
| 45 | + |
| 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)); |
| 49 | + } |
| 50 | +} |
| 51 | + |
17 | 52 | /**
|
18 | 53 | * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
|
19 | 54 | * @node: icc node to operate on
|
@@ -159,6 +194,36 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
|
159 | 194 | }
|
160 | 195 | EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
|
161 | 196 |
|
| 197 | +/** |
| 198 | + * qcom_icc_rpmh_configure_qos - configure QoS parameters |
| 199 | + * @qp: qcom icc provider associated with QoS endpoint nodes |
| 200 | + * |
| 201 | + * Return: 0 on success, or an error code otherwise |
| 202 | + */ |
| 203 | +static int qcom_icc_rpmh_configure_qos(struct qcom_icc_provider *qp) |
| 204 | +{ |
| 205 | + struct qcom_icc_node *qnode; |
| 206 | + size_t i; |
| 207 | + int ret; |
| 208 | + |
| 209 | + ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks); |
| 210 | + if (ret) |
| 211 | + return ret; |
| 212 | + |
| 213 | + for (i = 0; i < qp->num_nodes; i++) { |
| 214 | + qnode = qp->nodes[i]; |
| 215 | + if (!qnode) |
| 216 | + continue; |
| 217 | + |
| 218 | + if (qnode->qosbox) |
| 219 | + qcom_icc_set_qos(qp, qnode); |
| 220 | + } |
| 221 | + |
| 222 | + clk_bulk_disable_unprepare(qp->num_clks, qp->clks); |
| 223 | + |
| 224 | + return ret; |
| 225 | +} |
| 226 | + |
162 | 227 | int qcom_icc_rpmh_probe(struct platform_device *pdev)
|
163 | 228 | {
|
164 | 229 | const struct qcom_icc_desc *desc;
|
@@ -199,7 +264,9 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
|
199 | 264 |
|
200 | 265 | qp->dev = dev;
|
201 | 266 | qp->bcms = desc->bcms;
|
| 267 | + qp->nodes = desc->nodes; |
202 | 268 | qp->num_bcms = desc->num_bcms;
|
| 269 | + qp->num_nodes = desc->num_nodes; |
203 | 270 |
|
204 | 271 | qp->voter = of_bcm_voter_get(qp->dev, NULL);
|
205 | 272 | if (IS_ERR(qp->voter))
|
@@ -229,6 +296,32 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
|
229 | 296 | data->nodes[i] = node;
|
230 | 297 | }
|
231 | 298 |
|
| 299 | + if (desc->config) { |
| 300 | + struct resource *res; |
| 301 | + void __iomem *base; |
| 302 | + |
| 303 | + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); |
| 304 | + if (IS_ERR(base)) |
| 305 | + goto skip_qos_config; |
| 306 | + |
| 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; |
| 311 | + } |
| 312 | + |
| 313 | + qp->num_clks = devm_clk_bulk_get_all(qp->dev, &qp->clks); |
| 314 | + if (qp->num_clks < 0) { |
| 315 | + dev_info(dev, "Skipping QoS, failed to get clk: %d\n", qp->num_clks); |
| 316 | + goto skip_qos_config; |
| 317 | + } |
| 318 | + |
| 319 | + ret = qcom_icc_rpmh_configure_qos(qp); |
| 320 | + if (ret) |
| 321 | + dev_info(dev, "Failed to program QoS: %d\n", ret); |
| 322 | + } |
| 323 | + |
| 324 | +skip_qos_config: |
232 | 325 | ret = icc_provider_register(provider);
|
233 | 326 | if (ret)
|
234 | 327 | goto err_remove_nodes;
|
|
0 commit comments