On 18/01/2023 16:21, Konrad Dybcio wrote:
On 17.01.2023 23:58, Dmitry Baryshkov wrote:
Turn CBF into the interconnect provider. Scale CBF frequency (bandwidth)
according to CPU frequencies.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx>
---
drivers/clk/qcom/clk-cbf-8996.c | 141 +++++++++++++++++++++++++++++++-
1 file changed, 140 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/qcom/clk-cbf-8996.c b/drivers/clk/qcom/clk-cbf-8996.c
index 9cde0e660228..9e30311a310b 100644
--- a/drivers/clk/qcom/clk-cbf-8996.c
+++ b/drivers/clk/qcom/clk-cbf-8996.c
@@ -5,6 +5,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/interconnect-provider.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -225,6 +226,133 @@ static const struct regmap_config cbf_msm8996_regmap_config = {
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
+#ifdef CONFIG_INTERCONNECT
+struct qcom_msm8996_cbf_icc_provider {
+ struct icc_provider provider;
+ struct clk *clk;
+};
+
+#define to_qcom_cbf_provider(_provider) \
+ container_of(_provider, struct qcom_msm8996_cbf_icc_provider, provider)
+
+enum {
+ CBF_MASTER_NODE = 2000,
Where did the 2000 come from?
Pure random. This is an internal ID. OSM_L3 uses 10000 here.
+ CBF_SLAVE_NODE
+};
+
+#define CBF_NUM_NODES 2
+
+static int qcom_msm8996_cbf_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct qcom_msm8996_cbf_icc_provider *qp;
+
+ qp = to_qcom_cbf_provider(src->provider);
+
+ return clk_set_rate(qp->clk, icc_units_to_bps(dst->peak_bw));
+}
+
+static int qcom_msm8996_cbf_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
+{
+ struct qcom_msm8996_cbf_icc_provider *qp;
+
+ qp = to_qcom_cbf_provider(node->provider);
+ *peak = clk_get_rate(qp->clk) / 1000ULL;
+
+ return 0;
+}
+
+static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
+{
+ struct device *dev = &pdev->dev;
+ struct qcom_msm8996_cbf_icc_provider *qp;
+ struct icc_provider *provider;
+ struct icc_onecell_data *data;
+ struct icc_node *node;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ data = devm_kzalloc(dev, struct_size(data, nodes, CBF_NUM_NODES), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->num_nodes = CBF_NUM_NODES;
+
+ qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
+ if (!qp)
+ return -ENOMEM;
+
+ qp->clk = clk;
+
+ provider = &qp->provider;
+ provider->dev = dev;
+ provider->get_bw = qcom_msm8996_cbf_icc_get_bw;
+ provider->set = qcom_msm8996_cbf_set;
+ provider->aggregate = icc_std_aggregate;
+ provider->xlate = of_icc_xlate_onecell;
+ INIT_LIST_HEAD(&provider->nodes);
+ provider->data = data;
+
+ ret = icc_provider_add(provider);
+ if (ret) {
+ dev_err(dev, "error adding interconnect provider\n");
+ return ret;
+ }
+
+ node = icc_node_create(CBF_MASTER_NODE);
+ if (IS_ERR(node)) {
+ ret = PTR_ERR(node);
+ goto err;
+ }
+
+ node->name = "cbf_master";
+ icc_node_add(node, provider);
+ icc_link_create(node, CBF_SLAVE_NODE);
+ data->nodes[0] = node;
+
+ node = icc_node_create(CBF_SLAVE_NODE);
+ if (IS_ERR(node)) {
+ ret = PTR_ERR(node);
+ goto err;
+ }
+
+ node->name = "cbf_slave";
+ icc_node_add(node, provider);
+ data->nodes[1] = node;
+
+ platform_set_drvdata(pdev, provider);
+
+ return 0;
+
+err:
+ icc_nodes_remove(provider);
+ icc_provider_del(provider);
+
+ return ret;
+}
+
+static int qcom_msm8996_cbf_icc_remove(struct platform_device *pdev)
+{
+ struct icc_provider *provider = platform_get_drvdata(pdev);
+
+ icc_nodes_remove(provider);
+ icc_provider_del(provider);
+
+ return 0;
+}
+#else
+static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev)
+{
+ dev_warn(&pdev->dev, "interconnects support is disabled, CBF clock is fixed\n");
s/interconnects/interconnect
ack
Will cpufreq still work correctly with opp tables having bw
properties, the nodes having icc properties and the icc provider
not probing?
I presume CPUfreq will error out during probe with -EPROVE_DEFER.
And then, will the system not choke up with high CPU
and inadequately low CBF clocks?
The CBF clock is set high during init. It is then lowered by using the
icc/bandwidth.
Maybe `select INTERCONNECT_something_8996` would be better?
There is no separate interconnect driver, it is provided as a part of
this driver. And I didn't want to select INTERCONNECT.
Konrad
+
+ return 0;
+}
+#define qcom_msm8996_cbf_icc_remove(pdev) (0)
+#endif
+
static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
{
void __iomem *base;
@@ -283,7 +411,16 @@ static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
+ if (ret)
+ return ret;
+
+ return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw);
+}
+
+static int qcom_msm8996_cbf_remove(struct platform_device *pdev)
+{
+ return qcom_msm8996_cbf_icc_remove(pdev);
}
static const struct of_device_id qcom_msm8996_cbf_match_table[] = {
@@ -294,9 +431,11 @@ MODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table);
static struct platform_driver qcom_msm8996_cbf_driver = {
.probe = qcom_msm8996_cbf_probe,
+ .remove = qcom_msm8996_cbf_remove,
.driver = {
.name = "qcom-msm8996-cbf",
.of_match_table = qcom_msm8996_cbf_match_table,
+ .sync_state = icc_sync_state,
},
};
--
With best wishes
Dmitry