summaryrefslogtreecommitdiffstats
path: root/drivers/interconnect/qcom/icc-rpm.c
diff options
context:
space:
mode:
authorDmitry Baryshkov <dmitry.baryshkov@linaro.org>2021-09-04 02:24:14 +0300
committerGeorgi Djakov <djakov@kernel.org>2021-10-04 14:13:57 +0300
commit2b6c7d645118cba7719f16f3b0e4d4a555776f48 (patch)
treee3bb86264affbf126e1b5a6d192aff9bfa1ad4b2 /drivers/interconnect/qcom/icc-rpm.c
parent656ba110e164e1aab2aa4bec9baac51008c5d12c (diff)
downloadlinux-stable-2b6c7d645118cba7719f16f3b0e4d4a555776f48.tar.gz
linux-stable-2b6c7d645118cba7719f16f3b0e4d4a555776f48.tar.bz2
linux-stable-2b6c7d645118cba7719f16f3b0e4d4a555776f48.zip
interconnect: sdm660: merge common code into icc-rpm
Other RPM interconnect drivers might also use QoS support. Move AP-owned nodes support from SDM660 driver to common icc-rpm.c. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org> Tested-by: Marijn Suijten <marijn.suijten@somainline.org> Tested-by: Shawn Guo <shawn.guo@linaro.org> Link: https://lore.kernel.org/r/20210903232421.1384199-5-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov <djakov@kernel.org>
Diffstat (limited to 'drivers/interconnect/qcom/icc-rpm.c')
-rw-r--r--drivers/interconnect/qcom/icc-rpm.c241
1 files changed, 219 insertions, 22 deletions
diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
index 3049454685dc..96a160edece9 100644
--- a/drivers/interconnect/qcom/icc-rpm.c
+++ b/drivers/interconnect/qcom/icc-rpm.c
@@ -11,60 +11,228 @@
#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.h"
-static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
+/* BIMC QoS */
+#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n))
+#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n))
+#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i))
+
+#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000
+#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300
+#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3
+#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8
+#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f
+
+#define M_BKE_EN_EN_BMASK 0x1
+
+/* NoC QoS */
+#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000))
+#define NOC_QOS_PRIORITY_P1_MASK 0xc
+#define NOC_QOS_PRIORITY_P0_MASK 0x3
+#define NOC_QOS_PRIORITY_P1_SHIFT 0x2
+
+#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000))
+#define NOC_QOS_MODEn_MASK 0x3
+
+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)
{
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;
+ u32 mode = NOC_QOS_MODE_BYPASS;
+ u32 val = 0;
+ int i, rc = 0;
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);
+ 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;
+ }
- sum_bw = icc_units_to_bps(agg_avg);
- max_peak_bw = icc_units_to_bps(agg_peak);
+ /* 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_P1_MASK, val);
+ if (rc)
+ return rc;
+
+ return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
+ NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
+}
+
+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);
+}
- /* send bandwidth request message to the RPM processor */
- if (qn->mas_rpm_id != -1) {
+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);
+
+ 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,
- qn->mas_rpm_id,
+ mas_rpm_id,
sum_bw);
if (ret) {
pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
- qn->mas_rpm_id, ret);
+ mas_rpm_id, ret);
return ret;
}
}
- if (qn->slv_rpm_id != -1) {
+ if (slv_rpm_id != -1) {
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
RPM_BUS_SLAVE_REQ,
- qn->slv_rpm_id,
+ slv_rpm_id,
sum_bw);
if (ret) {
pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
- qn->slv_rpm_id, ret);
+ slv_rpm_id, ret);
return ret;
}
}
+ return ret;
+}
+
+static int qcom_icc_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);
@@ -115,8 +283,13 @@ int qnoc_probe(struct platform_device *pdev)
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
- cds = bus_clocks;
- cd_num = ARRAY_SIZE(bus_clocks);
+ if (desc->num_clocks) {
+ cds = desc->clocks;
+ cd_num = desc->num_clocks;
+ } else {
+ cds = bus_clocks;
+ cd_num = ARRAY_SIZE(bus_clocks);
+ }
qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL);
if (!qp)
@@ -131,6 +304,30 @@ int qnoc_probe(struct platform_device *pdev)
qp->bus_clks[i].id = cds[i];
qp->num_clks = cd_num;
+ qp->is_bimc_node = desc->is_bimc_node;
+
+ if (desc->regmap_cfg) {
+ struct resource *res;
+ void __iomem *mmio;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ mmio = devm_ioremap_resource(dev, res);
+
+ if (IS_ERR(mmio)) {
+ dev_err(dev, "Cannot ioremap interconnect bus resource\n");
+ return PTR_ERR(mmio);
+ }
+
+ qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg);
+ if (IS_ERR(qp->regmap)) {
+ dev_err(dev, "Cannot regmap interconnect bus resource\n");
+ return PTR_ERR(qp->regmap);
+ }
+ }
+
ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
if (ret)
return ret;