forked from torvalds/linux
Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
interconnect: qcom: sdm660: Commonize RPM-QoS
SoCs such as MSM8996 also control bus QoS in a similar fashion to SDM660, with some paths being controlled by RPM and others directly by the AP. Move relevant functions and defines to a new object so that they can be used in multiple drivers. Signed-off-by: Yassine Oudjana <y.oudjana@protonmail.com>
- Loading branch information
1 parent
0724ce5
commit de643987af1a3f016b17f6474470912caad563e9
Showing
5 changed files
with
380 additions
and
343 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
| /* | ||
| * Copyright (C) 2020, AngeloGioacchino Del Regno <kholk11@gmail.com> | ||
| */ | ||
|
|
||
| #include <linux/clk.h> | ||
| #include <linux/device.h> | ||
| #include <linux/interconnect-provider.h> | ||
| #include <linux/io.h> | ||
| #include <linux/module.h> | ||
| #include <linux/of_device.h> | ||
| #include <linux/of_platform.h> | ||
| #include <linux/platform_device.h> | ||
| #include <linux/regmap.h> | ||
| #include <linux/slab.h> | ||
|
|
||
| #include "smd-rpm.h" | ||
| #include "icc-rpm-qos.h" | ||
|
|
||
| static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, | ||
| struct qcom_icc_qos *qos, | ||
| int regnum) | ||
| { | ||
| u32 val; | ||
| u32 mask; | ||
|
|
||
| val = qos->prio_level; | ||
| mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; | ||
|
|
||
| val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; | ||
| mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; | ||
|
|
||
| /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ | ||
| if (regnum != 3) { | ||
| val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; | ||
| mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; | ||
| } | ||
|
|
||
| return regmap_update_bits(rmap, | ||
| M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), | ||
| mask, val); | ||
| } | ||
|
|
||
| static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, | ||
| bool bypass_mode) | ||
| { | ||
| struct qcom_icc_provider *qp; | ||
| struct qcom_icc_node *qn; | ||
| struct icc_provider *provider; | ||
| u32 mode = NOC_QOS_MODE_BYPASS; | ||
| u32 val = 0; | ||
| int i, rc = 0; | ||
|
|
||
| qn = src->data; | ||
| provider = src->provider; | ||
| qp = to_qcom_provider(provider); | ||
|
|
||
| if (qn->qos.qos_mode != -1) | ||
| mode = qn->qos.qos_mode; | ||
|
|
||
| /* QoS Priority: The QoS Health parameters are getting considered | ||
| * only if we are NOT in Bypass Mode. | ||
| */ | ||
| if (mode != NOC_QOS_MODE_BYPASS) { | ||
| for (i = 3; i >= 0; i--) { | ||
| rc = qcom_icc_bimc_set_qos_health(qp->regmap, | ||
| &qn->qos, i); | ||
| if (rc) | ||
| return rc; | ||
| } | ||
|
|
||
| /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ | ||
| val = 1; | ||
| } | ||
|
|
||
| return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), | ||
| M_BKE_EN_EN_BMASK, val); | ||
| } | ||
|
|
||
| static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, | ||
| struct qcom_icc_qos *qos) | ||
| { | ||
| u32 val; | ||
| int rc; | ||
|
|
||
| /* Must be updated one at a time, P1 first, P0 last */ | ||
| val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; | ||
| rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), | ||
| NOC_QOS_PRIORITY_MASK, val); | ||
| if (rc) | ||
| return rc; | ||
|
|
||
| val = qos->prio_level << NOC_QOS_PRIORITY_P0_SHIFT; | ||
| return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), | ||
| NOC_QOS_PRIORITY_MASK, val); | ||
| } | ||
|
|
||
| static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) | ||
| { | ||
| struct qcom_icc_provider *qp; | ||
| struct qcom_icc_node *qn; | ||
| struct icc_provider *provider; | ||
| u32 mode = NOC_QOS_MODE_BYPASS; | ||
| int rc = 0; | ||
|
|
||
| qn = src->data; | ||
| provider = src->provider; | ||
| qp = to_qcom_provider(provider); | ||
|
|
||
| if (qn->qos.qos_port < 0) { | ||
| dev_dbg(src->provider->dev, | ||
| "NoC QoS: Skipping %s: vote aggregated on parent.\n", | ||
| qn->name); | ||
| return 0; | ||
| } | ||
|
|
||
| if (qn->qos.qos_mode != -1) | ||
| mode = qn->qos.qos_mode; | ||
|
|
||
| if (mode == NOC_QOS_MODE_FIXED) { | ||
| dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", | ||
| qn->name); | ||
| rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); | ||
| if (rc) | ||
| return rc; | ||
| } else if (mode == NOC_QOS_MODE_BYPASS) { | ||
| dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", | ||
| qn->name); | ||
| } | ||
|
|
||
| return regmap_update_bits(qp->regmap, | ||
| NOC_QOS_MODEn_ADDR(qn->qos.qos_port), | ||
| NOC_QOS_MODEn_MASK, mode); | ||
| } | ||
|
|
||
| static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) | ||
| { | ||
| struct qcom_icc_provider *qp = to_qcom_provider(node->provider); | ||
| struct qcom_icc_node *qn = node->data; | ||
|
|
||
| dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); | ||
|
|
||
| if (qp->is_bimc_node) | ||
| return qcom_icc_set_bimc_qos(node, sum_bw, | ||
| (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS)); | ||
|
|
||
| return qcom_icc_set_noc_qos(node, sum_bw); | ||
| } | ||
|
|
||
| static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) | ||
| { | ||
| int ret = 0; | ||
|
|
||
| if (mas_rpm_id != -1) { | ||
| ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, | ||
| RPM_BUS_MASTER_REQ, | ||
| mas_rpm_id, | ||
| sum_bw); | ||
| if (ret) { | ||
| pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", | ||
| mas_rpm_id, ret); | ||
| return ret; | ||
| } | ||
| } | ||
|
|
||
| if (slv_rpm_id != -1) { | ||
| ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, | ||
| RPM_BUS_SLAVE_REQ, | ||
| slv_rpm_id, | ||
| sum_bw); | ||
| if (ret) { | ||
| pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", | ||
| slv_rpm_id, ret); | ||
| return ret; | ||
| } | ||
| } | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| int qcom_icc_rpm_qos_set(struct icc_node *src, struct icc_node *dst) | ||
| { | ||
| struct qcom_icc_provider *qp; | ||
| struct qcom_icc_node *qn; | ||
| struct icc_provider *provider; | ||
| struct icc_node *n; | ||
| u64 sum_bw; | ||
| u64 max_peak_bw; | ||
| u64 rate; | ||
| u32 agg_avg = 0; | ||
| u32 agg_peak = 0; | ||
| int ret, i; | ||
|
|
||
| qn = src->data; | ||
| provider = src->provider; | ||
| qp = to_qcom_provider(provider); | ||
|
|
||
| list_for_each_entry(n, &provider->nodes, node_list) | ||
| provider->aggregate(n, 0, n->avg_bw, n->peak_bw, | ||
| &agg_avg, &agg_peak); | ||
|
|
||
| sum_bw = icc_units_to_bps(agg_avg); | ||
| max_peak_bw = icc_units_to_bps(agg_peak); | ||
|
|
||
| if (!qn->qos.ap_owned) { | ||
| /* send bandwidth request message to the RPM processor */ | ||
| ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); | ||
| if (ret) | ||
| return ret; | ||
| } else if (qn->qos.qos_mode != -1) { | ||
| /* set bandwidth directly from the AP */ | ||
| ret = qcom_icc_qos_set(src, sum_bw); | ||
| if (ret) | ||
| return ret; | ||
| } | ||
|
|
||
| rate = max(sum_bw, max_peak_bw); | ||
|
|
||
| do_div(rate, qn->buswidth); | ||
|
|
||
| if (qn->rate == rate) | ||
| return 0; | ||
|
|
||
| for (i = 0; i < qp->num_clks; i++) { | ||
| ret = clk_set_rate(qp->bus_clks[i].clk, rate); | ||
| if (ret) { | ||
| pr_err("%s clk_set_rate error: %d\n", | ||
| qp->bus_clks[i].id, ret); | ||
| return ret; | ||
| } | ||
| } | ||
|
|
||
| qn->rate = rate; | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_GPL(qcom_icc_rpm_qos_set); |
Oops, something went wrong.