Skip to content
Permalink
Browse files
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>
  • Loading branch information
lumag authored and intel-lab-lkp committed Aug 18, 2021
1 parent a6911b8 commit d00e2bca640c9ad7926d58651be95a7b9cf7dec9
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 585 deletions.
@@ -11,60 +11,231 @@
#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_MASK 0xf
#define NOC_QOS_PRIORITY_P1_SHIFT 0x2
#define NOC_QOS_PRIORITY_P0_SHIFT 0x3

#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,
bool bypass_mode)
{
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_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;

/* send bandwidth request message to the RPM processor */
if (qn->mas_rpm_id != -1) {
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);
@@ -90,6 +261,10 @@ static const char * const bus_clocks[] = {
"bus", "bus_a",
};

static const char * const bus_mm_clocks[] = {
"bus", "bus_a", "iface",
};

int qnoc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -115,8 +290,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->has_iface_clk) {
cds = bus_mm_clocks;
cd_num = ARRAY_SIZE(bus_mm_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 +311,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;

0 comments on commit d00e2bc

Please sign in to comment.