Message ID | tBji8z2BZb2uc4O2OP7PyUJqbxLiWwoKnR7Ybshs@cp3-web-050.plabs.ch (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | interconnect: qcom: Add MSM8996 interconnect driver | expand |
Il 30/07/21 14:06, Yassine Oudjana ha scritto: > 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> Hello Yassine, thanks for the patch! However, I think there are a few things to improve: > --- > drivers/interconnect/qcom/Kconfig | 5 +- > drivers/interconnect/qcom/Makefile | 2 + > drivers/interconnect/qcom/icc-rpm-qos.c | 237 ++++++++++++++++ > drivers/interconnect/qcom/icc-rpm-qos.h | 133 +++++++++ > drivers/interconnect/qcom/icc-rpmh.c | 6 +- > drivers/interconnect/qcom/icc-rpmh.h | 2 +- > drivers/interconnect/qcom/sc7180.c | 2 +- > drivers/interconnect/qcom/sc7280.c | 2 +- > drivers/interconnect/qcom/sdm660.c | 346 +----------------------- > drivers/interconnect/qcom/sdm845.c | 2 +- > drivers/interconnect/qcom/sdx55.c | 2 +- > drivers/interconnect/qcom/sm8150.c | 2 +- > drivers/interconnect/qcom/sm8250.c | 2 +- > drivers/interconnect/qcom/sm8350.c | 2 +- > 14 files changed, 391 insertions(+), 354 deletions(-) > create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.c > create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.h > > diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig > index 0d7a2500d0b8..ad16224f1720 100644 > --- a/drivers/interconnect/qcom/Kconfig > +++ b/drivers/interconnect/qcom/Kconfig > @@ -87,7 +87,7 @@ config INTERCONNECT_QCOM_SDM660 > tristate "Qualcomm SDM660 interconnect driver" > depends on INTERCONNECT_QCOM > depends on QCOM_SMD_RPM > - select INTERCONNECT_QCOM_SMD_RPM > + select INTERCONNECT_QCOM_SMD_RPM_QOS > help > This is a driver for the Qualcomm Network-on-Chip on sdm660-based > platforms. > @@ -139,3 +139,6 @@ config INTERCONNECT_QCOM_SM8350 > > config INTERCONNECT_QCOM_SMD_RPM > tristate > + > +config INTERCONNECT_QCOM_SMD_RPM_QOS > + tristate > diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile > index 2880129a6fe4..2d04d024f46e 100644 > --- a/drivers/interconnect/qcom/Makefile > +++ b/drivers/interconnect/qcom/Makefile > @@ -16,6 +16,7 @@ qnoc-sm8150-objs := sm8150.o > qnoc-sm8250-objs := sm8250.o > qnoc-sm8350-objs := sm8350.o > icc-smd-rpm-objs := smd-rpm.o icc-rpm.o > +icc-smd-rpm-qos-objs := smd-rpm.o icc-rpm-qos.o > > obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o > obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o > @@ -33,3 +34,4 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o > obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o > obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o > obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o > +obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM_QOS) += icc-smd-rpm-qos.o > diff --git a/drivers/interconnect/qcom/icc-rpm-qos.c b/drivers/interconnect/qcom/icc-rpm-qos.c > new file mode 100644 > index 000000000000..678b347375d8 > --- /dev/null > +++ b/drivers/interconnect/qcom/icc-rpm-qos.c > @@ -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); > diff --git a/drivers/interconnect/qcom/icc-rpm-qos.h b/drivers/interconnect/qcom/icc-rpm-qos.h > new file mode 100644 > index 000000000000..625ac5f84ebc > --- /dev/null > +++ b/drivers/interconnect/qcom/icc-rpm-qos.h > @@ -0,0 +1,133 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020, AngeloGioacchino Del Regno <kholk11@gmail.com> > + */ > + > +#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ > +#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ > + > +#define RPM_BUS_MASTER_REQ 0x73616d62 > +#define RPM_BUS_SLAVE_REQ 0x766c7362 > + > +/* 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 > + > +/* Valid for both NoC and BIMC */ > +#define NOC_QOS_MODE_FIXED 0x0 > +#define NOC_QOS_MODE_LIMITER 0x1 > +#define NOC_QOS_MODE_BYPASS 0x2 > + > +/* NoC QoS */ > +#define NOC_PERM_MODE_FIXED 1 > +#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) > + > +#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 > + > +#define to_qcom_provider(_provider) \ > + container_of(_provider, struct qcom_icc_provider, provider) > + > +/** > + * struct qcom_icc_provider - Qualcomm specific interconnect provider > + * @provider: generic interconnect provider > + * @bus_clks: the clk_bulk_data table of bus clocks > + * @num_clks: the total number of clk_bulk_data entries > + * @is_bimc_node: indicates whether to use bimc specific setting > + * @regmap: regmap for QoS registers read/write access > + * @mmio: NoC base iospace > + */ > +struct qcom_icc_provider { > + struct icc_provider provider; > + struct clk_bulk_data *bus_clks; > + int num_clks; > + bool is_bimc_node; > + struct regmap *regmap; > + void __iomem *mmio; > +}; > + > +/** > + * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters > + * @areq_prio: node requests priority > + * @prio_level: priority level for bus communication > + * @limit_commands: activate/deactivate limiter mode during runtime > + * @ap_owned: indicates if the node is owned by the AP or by the RPM > + * @qos_mode: default qos mode for this node > + * @qos_port: qos port number for finding qos registers of this node > + */ > +struct qcom_icc_qos { > + u32 areq_prio; > + u32 prio_level; > + bool limit_commands; > + bool ap_owned; > + int qos_mode; > + int qos_port; > +}; > + > +/** > + * struct qcom_icc_node - Qualcomm specific interconnect nodes > + * @name: the node name used in debugfs > + * @id: a unique node identifier > + * @links: an array of nodes where we can go next while traversing > + * @num_links: the total number of @links > + * @buswidth: width of the interconnect between a node and the bus (bytes) > + * @mas_rpm_id: RPM id for devices that are bus masters > + * @slv_rpm_id: RPM id for devices that are bus slaves > + * @qos: NoC QoS setting parameters > + * @rate: current bus clock rate in Hz > + */ > + > +#define MAX_LINKS 38 > + > +struct qcom_icc_node { > + unsigned char *name; > + u16 id; > + u16 links[MAX_LINKS]; > + u16 num_links; > + u16 buswidth; > + int mas_rpm_id; > + int slv_rpm_id; > + struct qcom_icc_qos qos; > + u64 rate; > +}; > + > +struct qcom_icc_desc { > + struct qcom_icc_node **nodes; > + size_t num_nodes; > + const struct regmap_config *regmap_cfg; > +}; > + > +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ > + _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ > + static struct qcom_icc_node _name = { \ > + .name = #_name, \ > + .id = _id, \ > + .buswidth = _buswidth, \ > + .mas_rpm_id = _mas_rpm_id, \ > + .slv_rpm_id = _slv_rpm_id, \ > + .qos.ap_owned = _ap_owned, \ > + .qos.qos_mode = _qos_mode, \ > + .qos.areq_prio = _qos_prio, \ > + .qos.prio_level = _qos_prio, \ > + .qos.qos_port = _qos_port, \ > + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ > + .links = { __VA_ARGS__ }, \ > + } > + > +int qcom_icc_rpm_qos_set(struct icc_node *src, struct icc_node *dst); > + > +#endif > diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c > index bf01d09dba6c..6f93273109a3 100644 > --- a/drivers/interconnect/qcom/icc-rpmh.c > +++ b/drivers/interconnect/qcom/icc-rpmh.c > @@ -70,13 +70,13 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, > EXPORT_SYMBOL_GPL(qcom_icc_aggregate); > > /** > - * qcom_icc_set - set the constraints based on path > + * qcom_icc_rpmh_set - set the constraints based on path > * @src: source node for the path to set constraints on > * @dst: destination node for the path to set constraints on > * > * Return: 0 on success, or an error code otherwise > */ > -int qcom_icc_set(struct icc_node *src, struct icc_node *dst) > +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst) This change should be splitted to a different commit, as it is not part of any commonization of the RPM QoS functions. This is a "global" rename, so you should create a preparation commit for this change. > { > struct qcom_icc_provider *qp; > struct qcom_icc_node *qn; > @@ -99,7 +99,7 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst) > > return 0; > } > -EXPORT_SYMBOL_GPL(qcom_icc_set); > +EXPORT_SYMBOL_GPL(qcom_icc_rpmh_set); > > struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data) > { > diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h > index e5f61ab989e7..5dc1049dc065 100644 > --- a/drivers/interconnect/qcom/icc-rpmh.h > +++ b/drivers/interconnect/qcom/icc-rpmh.h > @@ -130,7 +130,7 @@ struct qcom_icc_desc { > > int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, > u32 peak_bw, u32 *agg_avg, u32 *agg_peak); > -int qcom_icc_set(struct icc_node *src, struct icc_node *dst); > +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst); Same for this. > struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data); > int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev); > void qcom_icc_pre_aggregate(struct icc_node *node); > diff --git a/drivers/interconnect/qcom/sc7180.c b/drivers/interconnect/qcom/sc7180.c > index 8d9044ed18ab..40232b41c080 100644 > --- a/drivers/interconnect/qcom/sc7180.c > +++ b/drivers/interconnect/qcom/sc7180.c > @@ -532,7 +532,7 @@ static int qnoc_probe(struct platform_device *pdev) > > provider = &qp->provider; > provider->dev = &pdev->dev; > - provider->set = qcom_icc_set; > + provider->set = qcom_icc_rpmh_set; And for this ... etc :)) > provider->pre_aggregate = qcom_icc_pre_aggregate; > provider->aggregate = qcom_icc_aggregate; > provider->xlate_extended = qcom_icc_xlate_extended; > diff --git a/drivers/interconnect/qcom/sc7280.c b/drivers/interconnect/qcom/sc7280.c > index 8d1b55c3705c..b84742debc13 100644 > --- a/drivers/interconnect/qcom/sc7280.c > +++ b/drivers/interconnect/qcom/sc7280.c > @@ -1830,7 +1830,7 @@ static int qnoc_probe(struct platform_device *pdev) > > provider = &qp->provider; > provider->dev = &pdev->dev; > - provider->set = qcom_icc_set; > + provider->set = qcom_icc_rpmh_set; > provider->pre_aggregate = qcom_icc_pre_aggregate; > provider->aggregate = qcom_icc_aggregate; > provider->xlate_extended = qcom_icc_xlate_extended; > diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c > index 632dbdd21915..acd026ecd17f 100644 > --- a/drivers/interconnect/qcom/sdm660.c > +++ b/drivers/interconnect/qcom/sdm660.c > @@ -4,7 +4,6 @@ > * Copyright (C) 2020, AngeloGioacchino Del Regno <kholk11@gmail.com> > */ > > -#include <dt-bindings/interconnect/qcom,sdm660.h> Why did you move this header down? If you have a reason, this is also not part of the RPM-QoS commonization. Thanks! - Angelo
On Fri, Jul 30 2021 at 17:39:31 +0400, AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org> wrote: > Il 30/07/21 14:06, Yassine Oudjana ha scritto: >> 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> > > Hello Yassine, > thanks for the patch! However, I think there are a few things to > improve: > >> --- >> drivers/interconnect/qcom/Kconfig | 5 +- >> drivers/interconnect/qcom/Makefile | 2 + >> drivers/interconnect/qcom/icc-rpm-qos.c | 237 ++++++++++++++++ >> drivers/interconnect/qcom/icc-rpm-qos.h | 133 +++++++++ >> drivers/interconnect/qcom/icc-rpmh.c | 6 +- >> drivers/interconnect/qcom/icc-rpmh.h | 2 +- >> drivers/interconnect/qcom/sc7180.c | 2 +- >> drivers/interconnect/qcom/sc7280.c | 2 +- >> drivers/interconnect/qcom/sdm660.c | 346 >> +----------------------- >> drivers/interconnect/qcom/sdm845.c | 2 +- >> drivers/interconnect/qcom/sdx55.c | 2 +- >> drivers/interconnect/qcom/sm8150.c | 2 +- >> drivers/interconnect/qcom/sm8250.c | 2 +- >> drivers/interconnect/qcom/sm8350.c | 2 +- >> 14 files changed, 391 insertions(+), 354 deletions(-) >> create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.c >> create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.h >> >> diff --git a/drivers/interconnect/qcom/Kconfig >> b/drivers/interconnect/qcom/Kconfig >> index 0d7a2500d0b8..ad16224f1720 100644 >> --- a/drivers/interconnect/qcom/Kconfig >> +++ b/drivers/interconnect/qcom/Kconfig >> @@ -87,7 +87,7 @@ config INTERCONNECT_QCOM_SDM660 >> tristate "Qualcomm SDM660 interconnect driver" >> depends on INTERCONNECT_QCOM >> depends on QCOM_SMD_RPM >> - select INTERCONNECT_QCOM_SMD_RPM >> + select INTERCONNECT_QCOM_SMD_RPM_QOS >> help >> This is a driver for the Qualcomm Network-on-Chip on >> sdm660-based >> platforms. >> @@ -139,3 +139,6 @@ config INTERCONNECT_QCOM_SM8350 >> >> config INTERCONNECT_QCOM_SMD_RPM >> tristate >> + >> +config INTERCONNECT_QCOM_SMD_RPM_QOS >> + tristate >> diff --git a/drivers/interconnect/qcom/Makefile >> b/drivers/interconnect/qcom/Makefile >> index 2880129a6fe4..2d04d024f46e 100644 >> --- a/drivers/interconnect/qcom/Makefile >> +++ b/drivers/interconnect/qcom/Makefile >> @@ -16,6 +16,7 @@ qnoc-sm8150-objs := sm8150.o >> qnoc-sm8250-objs := sm8250.o >> qnoc-sm8350-objs := sm8350.o >> icc-smd-rpm-objs := smd-rpm.o icc-rpm.o >> +icc-smd-rpm-qos-objs := smd-rpm.o icc-rpm-qos.o >> >> obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o >> obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o >> @@ -33,3 +34,4 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += >> qnoc-sm8150.o >> obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o >> obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o >> obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o >> +obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM_QOS) += icc-smd-rpm-qos.o >> diff --git a/drivers/interconnect/qcom/icc-rpm-qos.c >> b/drivers/interconnect/qcom/icc-rpm-qos.c >> new file mode 100644 >> index 000000000000..678b347375d8 >> --- /dev/null >> +++ b/drivers/interconnect/qcom/icc-rpm-qos.c >> @@ -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); >> diff --git a/drivers/interconnect/qcom/icc-rpm-qos.h >> b/drivers/interconnect/qcom/icc-rpm-qos.h >> new file mode 100644 >> index 000000000000..625ac5f84ebc >> --- /dev/null >> +++ b/drivers/interconnect/qcom/icc-rpm-qos.h >> @@ -0,0 +1,133 @@ >> +/* SPDX-License-Identifier: GPL-2.0 */ >> +/* >> + * Copyright (C) 2020, AngeloGioacchino Del Regno >> <kholk11@gmail.com> >> + */ >> + >> +#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ >> +#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ >> + >> +#define RPM_BUS_MASTER_REQ 0x73616d62 >> +#define RPM_BUS_SLAVE_REQ 0x766c7362 >> + >> +/* 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 >> + >> +/* Valid for both NoC and BIMC */ >> +#define NOC_QOS_MODE_FIXED 0x0 >> +#define NOC_QOS_MODE_LIMITER 0x1 >> +#define NOC_QOS_MODE_BYPASS 0x2 >> + >> +/* NoC QoS */ >> +#define NOC_PERM_MODE_FIXED 1 >> +#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) >> + >> +#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 >> + >> +#define to_qcom_provider(_provider) \ >> + container_of(_provider, struct qcom_icc_provider, provider) >> + >> +/** >> + * struct qcom_icc_provider - Qualcomm specific interconnect >> provider >> + * @provider: generic interconnect provider >> + * @bus_clks: the clk_bulk_data table of bus clocks >> + * @num_clks: the total number of clk_bulk_data entries >> + * @is_bimc_node: indicates whether to use bimc specific setting >> + * @regmap: regmap for QoS registers read/write access >> + * @mmio: NoC base iospace >> + */ >> +struct qcom_icc_provider { >> + struct icc_provider provider; >> + struct clk_bulk_data *bus_clks; >> + int num_clks; >> + bool is_bimc_node; >> + struct regmap *regmap; >> + void __iomem *mmio; >> +}; >> + >> +/** >> + * struct qcom_icc_qos - Qualcomm specific interconnect QoS >> parameters >> + * @areq_prio: node requests priority >> + * @prio_level: priority level for bus communication >> + * @limit_commands: activate/deactivate limiter mode during runtime >> + * @ap_owned: indicates if the node is owned by the AP or by the >> RPM >> + * @qos_mode: default qos mode for this node >> + * @qos_port: qos port number for finding qos registers of this >> node >> + */ >> +struct qcom_icc_qos { >> + u32 areq_prio; >> + u32 prio_level; >> + bool limit_commands; >> + bool ap_owned; >> + int qos_mode; >> + int qos_port; >> +}; >> + >> +/** >> + * struct qcom_icc_node - Qualcomm specific interconnect nodes >> + * @name: the node name used in debugfs >> + * @id: a unique node identifier >> + * @links: an array of nodes where we can go next while traversing >> + * @num_links: the total number of @links >> + * @buswidth: width of the interconnect between a node and the bus >> (bytes) >> + * @mas_rpm_id: RPM id for devices that are bus masters >> + * @slv_rpm_id: RPM id for devices that are bus slaves >> + * @qos: NoC QoS setting parameters >> + * @rate: current bus clock rate in Hz >> + */ >> + >> +#define MAX_LINKS 38 >> + >> +struct qcom_icc_node { >> + unsigned char *name; >> + u16 id; >> + u16 links[MAX_LINKS]; >> + u16 num_links; >> + u16 buswidth; >> + int mas_rpm_id; >> + int slv_rpm_id; >> + struct qcom_icc_qos qos; >> + u64 rate; >> +}; >> + >> +struct qcom_icc_desc { >> + struct qcom_icc_node **nodes; >> + size_t num_nodes; >> + const struct regmap_config *regmap_cfg; >> +}; >> + >> +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, >> _slv_rpm_id, \ >> + _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ >> + static struct qcom_icc_node _name = { \ >> + .name = #_name, \ >> + .id = _id, \ >> + .buswidth = _buswidth, \ >> + .mas_rpm_id = _mas_rpm_id, \ >> + .slv_rpm_id = _slv_rpm_id, \ >> + .qos.ap_owned = _ap_owned, \ >> + .qos.qos_mode = _qos_mode, \ >> + .qos.areq_prio = _qos_prio, \ >> + .qos.prio_level = _qos_prio, \ >> + .qos.qos_port = _qos_port, \ >> + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ >> + .links = { __VA_ARGS__ }, \ >> + } >> + >> +int qcom_icc_rpm_qos_set(struct icc_node *src, struct icc_node >> *dst); >> + >> +#endif >> diff --git a/drivers/interconnect/qcom/icc-rpmh.c >> b/drivers/interconnect/qcom/icc-rpmh.c >> index bf01d09dba6c..6f93273109a3 100644 >> --- a/drivers/interconnect/qcom/icc-rpmh.c >> +++ b/drivers/interconnect/qcom/icc-rpmh.c >> @@ -70,13 +70,13 @@ int qcom_icc_aggregate(struct icc_node *node, >> u32 tag, u32 avg_bw, >> EXPORT_SYMBOL_GPL(qcom_icc_aggregate); >> >> /** >> - * qcom_icc_set - set the constraints based on path >> + * qcom_icc_rpmh_set - set the constraints based on path >> * @src: source node for the path to set constraints on >> * @dst: destination node for the path to set constraints on >> * >> * Return: 0 on success, or an error code otherwise >> */ >> -int qcom_icc_set(struct icc_node *src, struct icc_node *dst) >> +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst) > > This change should be splitted to a different commit, as it is not > part of any > commonization of the RPM QoS functions. This is a "global" rename, so > you should > create a preparation commit for this change. > Noted. >> { >> struct qcom_icc_provider *qp; >> struct qcom_icc_node *qn; >> @@ -99,7 +99,7 @@ int qcom_icc_set(struct icc_node *src, struct >> icc_node *dst) >> >> return 0; >> } >> -EXPORT_SYMBOL_GPL(qcom_icc_set); >> +EXPORT_SYMBOL_GPL(qcom_icc_rpmh_set); >> >> struct icc_node_data *qcom_icc_xlate_extended(struct >> of_phandle_args *spec, void *data) >> { >> diff --git a/drivers/interconnect/qcom/icc-rpmh.h >> b/drivers/interconnect/qcom/icc-rpmh.h >> index e5f61ab989e7..5dc1049dc065 100644 >> --- a/drivers/interconnect/qcom/icc-rpmh.h >> +++ b/drivers/interconnect/qcom/icc-rpmh.h >> @@ -130,7 +130,7 @@ struct qcom_icc_desc { >> >> int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, >> u32 peak_bw, u32 *agg_avg, u32 *agg_peak); >> -int qcom_icc_set(struct icc_node *src, struct icc_node *dst); >> +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst); > > Same for this. > >> struct icc_node_data *qcom_icc_xlate_extended(struct >> of_phandle_args *spec, void *data); >> int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device >> *dev); >> void qcom_icc_pre_aggregate(struct icc_node *node); >> diff --git a/drivers/interconnect/qcom/sc7180.c >> b/drivers/interconnect/qcom/sc7180.c >> index 8d9044ed18ab..40232b41c080 100644 >> --- a/drivers/interconnect/qcom/sc7180.c >> +++ b/drivers/interconnect/qcom/sc7180.c >> @@ -532,7 +532,7 @@ static int qnoc_probe(struct platform_device >> *pdev) >> >> provider = &qp->provider; >> provider->dev = &pdev->dev; >> - provider->set = qcom_icc_set; >> + provider->set = qcom_icc_rpmh_set; > > And for this ... etc :)) > >> provider->pre_aggregate = qcom_icc_pre_aggregate; >> provider->aggregate = qcom_icc_aggregate; >> provider->xlate_extended = qcom_icc_xlate_extended; >> diff --git a/drivers/interconnect/qcom/sc7280.c >> b/drivers/interconnect/qcom/sc7280.c >> index 8d1b55c3705c..b84742debc13 100644 >> --- a/drivers/interconnect/qcom/sc7280.c >> +++ b/drivers/interconnect/qcom/sc7280.c >> @@ -1830,7 +1830,7 @@ static int qnoc_probe(struct platform_device >> *pdev) >> >> provider = &qp->provider; >> provider->dev = &pdev->dev; >> - provider->set = qcom_icc_set; >> + provider->set = qcom_icc_rpmh_set; >> provider->pre_aggregate = qcom_icc_pre_aggregate; >> provider->aggregate = qcom_icc_aggregate; >> provider->xlate_extended = qcom_icc_xlate_extended; >> diff --git a/drivers/interconnect/qcom/sdm660.c >> b/drivers/interconnect/qcom/sdm660.c >> index 632dbdd21915..acd026ecd17f 100644 >> --- a/drivers/interconnect/qcom/sdm660.c >> +++ b/drivers/interconnect/qcom/sdm660.c >> @@ -4,7 +4,6 @@ >> * Copyright (C) 2020, AngeloGioacchino Del Regno >> <kholk11@gmail.com> >> */ >> >> -#include <dt-bindings/interconnect/qcom,sdm660.h> > > Why did you move this header down? > If you have a reason, this is also not part of the RPM-QoS > commonization. > Must have been by mistake. I tried to keep the order of things in msm8996.c similar to newer drivers, and I probably ended up editing sdm660.c as well without noticing it. > Thanks! > - Angelo
diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 0d7a2500d0b8..ad16224f1720 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -87,7 +87,7 @@ config INTERCONNECT_QCOM_SDM660 tristate "Qualcomm SDM660 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM - select INTERCONNECT_QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM_QOS help This is a driver for the Qualcomm Network-on-Chip on sdm660-based platforms. @@ -139,3 +139,6 @@ config INTERCONNECT_QCOM_SM8350 config INTERCONNECT_QCOM_SMD_RPM tristate + +config INTERCONNECT_QCOM_SMD_RPM_QOS + tristate diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 2880129a6fe4..2d04d024f46e 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -16,6 +16,7 @@ qnoc-sm8150-objs := sm8150.o qnoc-sm8250-objs := sm8250.o qnoc-sm8350-objs := sm8350.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o +icc-smd-rpm-qos-objs := smd-rpm.o icc-rpm-qos.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o @@ -33,3 +34,4 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o +obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM_QOS) += icc-smd-rpm-qos.o diff --git a/drivers/interconnect/qcom/icc-rpm-qos.c b/drivers/interconnect/qcom/icc-rpm-qos.c new file mode 100644 index 000000000000..678b347375d8 --- /dev/null +++ b/drivers/interconnect/qcom/icc-rpm-qos.c @@ -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); diff --git a/drivers/interconnect/qcom/icc-rpm-qos.h b/drivers/interconnect/qcom/icc-rpm-qos.h new file mode 100644 index 000000000000..625ac5f84ebc --- /dev/null +++ b/drivers/interconnect/qcom/icc-rpm-qos.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020, AngeloGioacchino Del Regno <kholk11@gmail.com> + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ +#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_QOS_H__ + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +/* 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 + +/* Valid for both NoC and BIMC */ +#define NOC_QOS_MODE_FIXED 0x0 +#define NOC_QOS_MODE_LIMITER 0x1 +#define NOC_QOS_MODE_BYPASS 0x2 + +/* NoC QoS */ +#define NOC_PERM_MODE_FIXED 1 +#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) + +#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 + +#define to_qcom_provider(_provider) \ + container_of(_provider, struct qcom_icc_provider, provider) + +/** + * struct qcom_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + * @is_bimc_node: indicates whether to use bimc specific setting + * @regmap: regmap for QoS registers read/write access + * @mmio: NoC base iospace + */ +struct qcom_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; + bool is_bimc_node; + struct regmap *regmap; + void __iomem *mmio; +}; + +/** + * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters + * @areq_prio: node requests priority + * @prio_level: priority level for bus communication + * @limit_commands: activate/deactivate limiter mode during runtime + * @ap_owned: indicates if the node is owned by the AP or by the RPM + * @qos_mode: default qos mode for this node + * @qos_port: qos port number for finding qos registers of this node + */ +struct qcom_icc_qos { + u32 areq_prio; + u32 prio_level; + bool limit_commands; + bool ap_owned; + int qos_mode; + int qos_port; +}; + +/** + * struct qcom_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @mas_rpm_id: RPM id for devices that are bus masters + * @slv_rpm_id: RPM id for devices that are bus slaves + * @qos: NoC QoS setting parameters + * @rate: current bus clock rate in Hz + */ + +#define MAX_LINKS 38 + +struct qcom_icc_node { + unsigned char *name; + u16 id; + u16 links[MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + struct qcom_icc_qos qos; + u64 rate; +}; + +struct qcom_icc_desc { + struct qcom_icc_node **nodes; + size_t num_nodes; + const struct regmap_config *regmap_cfg; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ + static struct qcom_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .qos.ap_owned = _ap_owned, \ + .qos.qos_mode = _qos_mode, \ + .qos.areq_prio = _qos_prio, \ + .qos.prio_level = _qos_prio, \ + .qos.qos_port = _qos_port, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +int qcom_icc_rpm_qos_set(struct icc_node *src, struct icc_node *dst); + +#endif diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index bf01d09dba6c..6f93273109a3 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -70,13 +70,13 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, EXPORT_SYMBOL_GPL(qcom_icc_aggregate); /** - * qcom_icc_set - set the constraints based on path + * qcom_icc_rpmh_set - set the constraints based on path * @src: source node for the path to set constraints on * @dst: destination node for the path to set constraints on * * Return: 0 on success, or an error code otherwise */ -int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst) { struct qcom_icc_provider *qp; struct qcom_icc_node *qn; @@ -99,7 +99,7 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst) return 0; } -EXPORT_SYMBOL_GPL(qcom_icc_set); +EXPORT_SYMBOL_GPL(qcom_icc_rpmh_set); struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data) { diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index e5f61ab989e7..5dc1049dc065 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -130,7 +130,7 @@ struct qcom_icc_desc { int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, u32 peak_bw, u32 *agg_avg, u32 *agg_peak); -int qcom_icc_set(struct icc_node *src, struct icc_node *dst); +int qcom_icc_rpmh_set(struct icc_node *src, struct icc_node *dst); struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data); int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev); void qcom_icc_pre_aggregate(struct icc_node *node); diff --git a/drivers/interconnect/qcom/sc7180.c b/drivers/interconnect/qcom/sc7180.c index 8d9044ed18ab..40232b41c080 100644 --- a/drivers/interconnect/qcom/sc7180.c +++ b/drivers/interconnect/qcom/sc7180.c @@ -532,7 +532,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; diff --git a/drivers/interconnect/qcom/sc7280.c b/drivers/interconnect/qcom/sc7280.c index 8d1b55c3705c..b84742debc13 100644 --- a/drivers/interconnect/qcom/sc7280.c +++ b/drivers/interconnect/qcom/sc7280.c @@ -1830,7 +1830,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 632dbdd21915..acd026ecd17f 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -4,7 +4,6 @@ * Copyright (C) 2020, AngeloGioacchino Del Regno <kholk11@gmail.com> */ -#include <dt-bindings/interconnect/qcom,sdm660.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/interconnect-provider.h> @@ -14,42 +13,11 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/slab.h> - -#include "smd-rpm.h" - -#define RPM_BUS_MASTER_REQ 0x73616d62 -#define RPM_BUS_SLAVE_REQ 0x766c7362 - -/* 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 - -/* Valid for both NoC and BIMC */ -#define NOC_QOS_MODE_FIXED 0x0 -#define NOC_QOS_MODE_LIMITER 0x1 -#define NOC_QOS_MODE_BYPASS 0x2 - -/* NoC QoS */ -#define NOC_PERM_MODE_FIXED 1 -#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) - -#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 +#include <dt-bindings/interconnect/qcom,sdm660.h> -#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) -#define NOC_QOS_MODEn_MASK 0x3 +#include "icc-rpm-qos.h" +#include "smd-rpm.h" enum { SDM660_MASTER_IPA = 1, @@ -159,9 +127,6 @@ enum { SDM660_SNOC, }; -#define to_qcom_provider(_provider) \ - container_of(_provider, struct qcom_icc_provider, provider) - static const struct clk_bulk_data bus_clocks[] = { { .id = "bus" }, { .id = "bus_a" }, @@ -173,91 +138,6 @@ static const struct clk_bulk_data bus_mm_clocks[] = { { .id = "iface" }, }; -/** - * struct qcom_icc_provider - Qualcomm specific interconnect provider - * @provider: generic interconnect provider - * @bus_clks: the clk_bulk_data table of bus clocks - * @num_clks: the total number of clk_bulk_data entries - * @is_bimc_node: indicates whether to use bimc specific setting - * @regmap: regmap for QoS registers read/write access - * @mmio: NoC base iospace - */ -struct qcom_icc_provider { - struct icc_provider provider; - struct clk_bulk_data *bus_clks; - int num_clks; - bool is_bimc_node; - struct regmap *regmap; - void __iomem *mmio; -}; - -#define SDM660_MAX_LINKS 34 - -/** - * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters - * @areq_prio: node requests priority - * @prio_level: priority level for bus communication - * @limit_commands: activate/deactivate limiter mode during runtime - * @ap_owned: indicates if the node is owned by the AP or by the RPM - * @qos_mode: default qos mode for this node - * @qos_port: qos port number for finding qos registers of this node - */ -struct qcom_icc_qos { - u32 areq_prio; - u32 prio_level; - bool limit_commands; - bool ap_owned; - int qos_mode; - int qos_port; -}; - -/** - * struct qcom_icc_node - Qualcomm specific interconnect nodes - * @name: the node name used in debugfs - * @id: a unique node identifier - * @links: an array of nodes where we can go next while traversing - * @num_links: the total number of @links - * @buswidth: width of the interconnect between a node and the bus (bytes) - * @mas_rpm_id: RPM id for devices that are bus masters - * @slv_rpm_id: RPM id for devices that are bus slaves - * @qos: NoC QoS setting parameters - * @rate: current bus clock rate in Hz - */ -struct qcom_icc_node { - unsigned char *name; - u16 id; - u16 links[SDM660_MAX_LINKS]; - u16 num_links; - u16 buswidth; - int mas_rpm_id; - int slv_rpm_id; - struct qcom_icc_qos qos; - u64 rate; -}; - -struct qcom_icc_desc { - struct qcom_icc_node **nodes; - size_t num_nodes; - const struct regmap_config *regmap_cfg; -}; - -#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ - _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ - static struct qcom_icc_node _name = { \ - .name = #_name, \ - .id = _id, \ - .buswidth = _buswidth, \ - .mas_rpm_id = _mas_rpm_id, \ - .slv_rpm_id = _slv_rpm_id, \ - .qos.ap_owned = _ap_owned, \ - .qos.qos_mode = _qos_mode, \ - .qos.areq_prio = _qos_prio, \ - .qos.prio_level = _qos_prio, \ - .qos.qos_port = _qos_port, \ - .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ - .links = { __VA_ARGS__ }, \ - } - DEFINE_QNODE(mas_ipa, SDM660_MASTER_IPA, 8, 59, -1, true, NOC_QOS_MODE_FIXED, 1, 3, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_cnoc_a2noc, SDM660_MASTER_CNOC_A2NOC, 8, 146, -1, true, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); DEFINE_QNODE(mas_sdcc_1, SDM660_MASTER_SDCC_1, 8, 33, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); @@ -555,224 +435,6 @@ static struct qcom_icc_desc sdm660_snoc = { .regmap_cfg = &sdm660_snoc_regmap_config, }; -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; -} - -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); - - 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; -} - static int qnoc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -848,7 +510,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; INIT_LIST_HEAD(&provider->nodes); provider->dev = dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpm_qos_set; provider->aggregate = icc_std_aggregate; provider->xlate = of_icc_xlate_onecell; provider->data = data; diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 366870150cbd..47b46601888a 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -469,7 +469,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; diff --git a/drivers/interconnect/qcom/sdx55.c b/drivers/interconnect/qcom/sdx55.c index a5a122ee3d21..11b0c109c7ca 100644 --- a/drivers/interconnect/qcom/sdx55.c +++ b/drivers/interconnect/qcom/sdx55.c @@ -263,7 +263,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate = of_icc_xlate_onecell; diff --git a/drivers/interconnect/qcom/sm8150.c b/drivers/interconnect/qcom/sm8150.c index c76b2c7f9b10..21410a20431b 100644 --- a/drivers/interconnect/qcom/sm8150.c +++ b/drivers/interconnect/qcom/sm8150.c @@ -530,7 +530,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate = of_icc_xlate_onecell; diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c index cc558fec74e3..8ec8504b48c6 100644 --- a/drivers/interconnect/qcom/sm8250.c +++ b/drivers/interconnect/qcom/sm8250.c @@ -546,7 +546,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate = of_icc_xlate_onecell; diff --git a/drivers/interconnect/qcom/sm8350.c b/drivers/interconnect/qcom/sm8350.c index 579b6ce8e046..e3f54e07aca7 100644 --- a/drivers/interconnect/qcom/sm8350.c +++ b/drivers/interconnect/qcom/sm8350.c @@ -538,7 +538,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; - provider->set = qcom_icc_set; + provider->set = qcom_icc_rpmh_set; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate = of_icc_xlate_onecell;
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> --- drivers/interconnect/qcom/Kconfig | 5 +- drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/icc-rpm-qos.c | 237 ++++++++++++++++ drivers/interconnect/qcom/icc-rpm-qos.h | 133 +++++++++ drivers/interconnect/qcom/icc-rpmh.c | 6 +- drivers/interconnect/qcom/icc-rpmh.h | 2 +- drivers/interconnect/qcom/sc7180.c | 2 +- drivers/interconnect/qcom/sc7280.c | 2 +- drivers/interconnect/qcom/sdm660.c | 346 +----------------------- drivers/interconnect/qcom/sdm845.c | 2 +- drivers/interconnect/qcom/sdx55.c | 2 +- drivers/interconnect/qcom/sm8150.c | 2 +- drivers/interconnect/qcom/sm8250.c | 2 +- drivers/interconnect/qcom/sm8350.c | 2 +- 14 files changed, 391 insertions(+), 354 deletions(-) create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.c create mode 100644 drivers/interconnect/qcom/icc-rpm-qos.h