Skip to content

Commit 9285221

Browse files
committed
Merge tag 'icc-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc into char-misc-next
Georgi writes: interconnect changes for 6.5 This pull request contains the interconnect changes for the 6.5-rc1 merge window which is a mix of core and driver changes with the following highlights: - Support for configuring QoS on the Qualcomm's RPM-based platforms, that required special handling of some interface (non-scaling) clocks. - Support for clock-based interconnect providers for cases when clock corresponds to bus bandwidth. This is used to enable CPU cluster bandwidth scaling on MSM8996 platforms. One patch is touching a file in the clock subsystem that has been acked by the maintainer. Core changes: interconnect: add clk-based icc provider support interconnect: icc-clk: fix modular build interconnect: drop unused icc_get() interface Driver changes: interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks interconnect: qcom: rpm: Drop unused parameters interconnect: qcom: rpm: Set QoS registers only once interconnect: qcom: rpm: Handle interface clocks interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore interconnect: qcom: msm8996: Promote to core_initcall interconnect: qcom: rpm: allocate enough data in probe() dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes Signed-off-by: Georgi Djakov <djakov@kernel.org> * tag 'icc-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc: dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes interconnect: icc-clk: fix modular build clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq interconnect: drop unused icc_get() interface interconnect: qcom: rpm: allocate enough data in probe() interconnect: qcom: msm8996: Promote to core_initcall interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks interconnect: qcom: rpm: Handle interface clocks interconnect: add clk-based icc provider support dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF interconnect: qcom: rpm: Set QoS registers only once interconnect: qcom: rpm: Drop unused parameters interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks
2 parents a5052c8 + 1400725 commit 9285221

14 files changed

Lines changed: 382 additions & 143 deletions

File tree

Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ properties:
5151
type: object
5252

5353
fsl,ddrc:
54-
$ref: "/schemas/types.yaml#/definitions/phandle"
54+
$ref: /schemas/types.yaml#/definitions/phandle
5555
description:
5656
Phandle to DDR Controller.
5757

drivers/clk/qcom/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ config QCOM_CLK_APCS_MSM8916
4848
config QCOM_CLK_APCC_MSM8996
4949
tristate "MSM8996 CPU Clock Controller"
5050
select QCOM_KRYO_L2_ACCESSORS
51+
select INTERCONNECT_CLK if INTERCONNECT
5152
depends on ARM64
5253
help
5354
Support for the CPU clock controller on msm8996 devices.

drivers/clk/qcom/clk-cbf-8996.c

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
#include <linux/bitfield.h>
66
#include <linux/clk.h>
77
#include <linux/clk-provider.h>
8+
#include <linux/interconnect-clk.h>
9+
#include <linux/interconnect-provider.h>
810
#include <linux/of.h>
911
#include <linux/module.h>
1012
#include <linux/platform_device.h>
1113
#include <linux/regmap.h>
1214

15+
#include <dt-bindings/interconnect/qcom,msm8996-cbf.h>
16+
1317
#include "clk-alpha-pll.h"
1418
#include "clk-regmap.h"
1519

@@ -223,6 +227,49 @@ static const struct regmap_config cbf_msm8996_regmap_config = {
223227
.val_format_endian = REGMAP_ENDIAN_LITTLE,
224228
};
225229

230+
#ifdef CONFIG_INTERCONNECT
231+
232+
/* Random ID that doesn't clash with main qnoc and OSM */
233+
#define CBF_MASTER_NODE 2000
234+
235+
static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
236+
{
237+
struct device *dev = &pdev->dev;
238+
struct clk *clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf");
239+
const struct icc_clk_data data[] = {
240+
{ .clk = clk, .name = "cbf", },
241+
};
242+
struct icc_provider *provider;
243+
244+
provider = icc_clk_register(dev, CBF_MASTER_NODE, ARRAY_SIZE(data), data);
245+
if (IS_ERR(provider))
246+
return PTR_ERR(provider);
247+
248+
platform_set_drvdata(pdev, provider);
249+
250+
return 0;
251+
}
252+
253+
static int qcom_msm8996_cbf_icc_remove(struct platform_device *pdev)
254+
{
255+
struct icc_provider *provider = platform_get_drvdata(pdev);
256+
257+
icc_clk_unregister(provider);
258+
259+
return 0;
260+
}
261+
#define qcom_msm8996_cbf_icc_sync_state icc_sync_state
262+
#else
263+
static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
264+
{
265+
dev_warn(&pdev->dev, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n");
266+
267+
return 0;
268+
}
269+
#define qcom_msm8996_cbf_icc_remove(pdev) (0)
270+
#define qcom_msm8996_cbf_icc_sync_state NULL
271+
#endif
272+
226273
static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
227274
{
228275
void __iomem *base;
@@ -281,7 +328,16 @@ static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
281328
if (ret)
282329
return ret;
283330

284-
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
331+
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
332+
if (ret)
333+
return ret;
334+
335+
return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw);
336+
}
337+
338+
static int qcom_msm8996_cbf_remove(struct platform_device *pdev)
339+
{
340+
return qcom_msm8996_cbf_icc_remove(pdev);
285341
}
286342

287343
static const struct of_device_id qcom_msm8996_cbf_match_table[] = {
@@ -292,9 +348,11 @@ MODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table);
292348

293349
static struct platform_driver qcom_msm8996_cbf_driver = {
294350
.probe = qcom_msm8996_cbf_probe,
351+
.remove = qcom_msm8996_cbf_remove,
295352
.driver = {
296353
.name = "qcom-msm8996-cbf",
297354
.of_match_table = qcom_msm8996_cbf_match_table,
355+
.sync_state = qcom_msm8996_cbf_icc_sync_state,
298356
},
299357
};
300358

drivers/interconnect/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ source "drivers/interconnect/imx/Kconfig"
1515
source "drivers/interconnect/qcom/Kconfig"
1616
source "drivers/interconnect/samsung/Kconfig"
1717

18+
config INTERCONNECT_CLK
19+
tristate
20+
depends on COMMON_CLK
21+
help
22+
Support for wrapping clocks into the interconnect nodes.
23+
1824
endif

drivers/interconnect/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ obj-$(CONFIG_INTERCONNECT) += icc-core.o
77
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
88
obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
99
obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/
10+
11+
obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o

drivers/interconnect/core.c

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ EXPORT_SYMBOL_GPL(icc_set_tag);
587587

588588
/**
589589
* icc_get_name() - Get name of the icc path
590-
* @path: reference to the path returned by icc_get()
590+
* @path: interconnect path
591591
*
592592
* This function is used by an interconnect consumer to get the name of the icc
593593
* path.
@@ -605,7 +605,7 @@ EXPORT_SYMBOL_GPL(icc_get_name);
605605

606606
/**
607607
* icc_set_bw() - set bandwidth constraints on an interconnect path
608-
* @path: reference to the path returned by icc_get()
608+
* @path: interconnect path
609609
* @avg_bw: average bandwidth in kilobytes per second
610610
* @peak_bw: peak bandwidth in kilobytes per second
611611
*
@@ -704,54 +704,6 @@ int icc_disable(struct icc_path *path)
704704
}
705705
EXPORT_SYMBOL_GPL(icc_disable);
706706

707-
/**
708-
* icc_get() - return a handle for path between two endpoints
709-
* @dev: the device requesting the path
710-
* @src_id: source device port id
711-
* @dst_id: destination device port id
712-
*
713-
* This function will search for a path between two endpoints and return an
714-
* icc_path handle on success. Use icc_put() to release
715-
* constraints when they are not needed anymore.
716-
* If the interconnect API is disabled, NULL is returned and the consumer
717-
* drivers will still build. Drivers are free to handle this specifically,
718-
* but they don't have to.
719-
*
720-
* Return: icc_path pointer on success, ERR_PTR() on error or NULL if the
721-
* interconnect API is disabled.
722-
*/
723-
struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id)
724-
{
725-
struct icc_node *src, *dst;
726-
struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
727-
728-
mutex_lock(&icc_lock);
729-
730-
src = node_find(src_id);
731-
if (!src)
732-
goto out;
733-
734-
dst = node_find(dst_id);
735-
if (!dst)
736-
goto out;
737-
738-
path = path_find(dev, src, dst);
739-
if (IS_ERR(path)) {
740-
dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
741-
goto out;
742-
}
743-
744-
path->name = kasprintf(GFP_KERNEL, "%s-%s", src->name, dst->name);
745-
if (!path->name) {
746-
kfree(path);
747-
path = ERR_PTR(-ENOMEM);
748-
}
749-
out:
750-
mutex_unlock(&icc_lock);
751-
return path;
752-
}
753-
EXPORT_SYMBOL_GPL(icc_get);
754-
755707
/**
756708
* icc_put() - release the reference to the icc_path
757709
* @path: interconnect path

drivers/interconnect/icc-clk.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (c) 2023, Linaro Ltd.
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/device.h>
8+
#include <linux/interconnect-clk.h>
9+
#include <linux/interconnect-provider.h>
10+
11+
struct icc_clk_node {
12+
struct clk *clk;
13+
bool enabled;
14+
};
15+
16+
struct icc_clk_provider {
17+
struct icc_provider provider;
18+
int num_clocks;
19+
struct icc_clk_node clocks[];
20+
};
21+
22+
#define to_icc_clk_provider(_provider) \
23+
container_of(_provider, struct icc_clk_provider, provider)
24+
25+
static int icc_clk_set(struct icc_node *src, struct icc_node *dst)
26+
{
27+
struct icc_clk_node *qn = src->data;
28+
int ret;
29+
30+
if (!qn || !qn->clk)
31+
return 0;
32+
33+
if (!src->peak_bw) {
34+
if (qn->enabled)
35+
clk_disable_unprepare(qn->clk);
36+
qn->enabled = false;
37+
38+
return 0;
39+
}
40+
41+
if (!qn->enabled) {
42+
ret = clk_prepare_enable(qn->clk);
43+
if (ret)
44+
return ret;
45+
qn->enabled = true;
46+
}
47+
48+
return clk_set_rate(qn->clk, icc_units_to_bps(src->peak_bw));
49+
}
50+
51+
static int icc_clk_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
52+
{
53+
struct icc_clk_node *qn = node->data;
54+
55+
if (!qn || !qn->clk)
56+
*peak = INT_MAX;
57+
else
58+
*peak = Bps_to_icc(clk_get_rate(qn->clk));
59+
60+
return 0;
61+
}
62+
63+
/**
64+
* icc_clk_register() - register a new clk-based interconnect provider
65+
* @dev: device supporting this provider
66+
* @first_id: an ID of the first provider's node
67+
* @num_clocks: number of instances of struct icc_clk_data
68+
* @data: data for the provider
69+
*
70+
* Registers and returns a clk-based interconnect provider. It is a simple
71+
* wrapper around COMMON_CLK framework, allowing other devices to vote on the
72+
* clock rate.
73+
*
74+
* Return: 0 on success, or an error code otherwise
75+
*/
76+
struct icc_provider *icc_clk_register(struct device *dev,
77+
unsigned int first_id,
78+
unsigned int num_clocks,
79+
const struct icc_clk_data *data)
80+
{
81+
struct icc_clk_provider *qp;
82+
struct icc_provider *provider;
83+
struct icc_onecell_data *onecell;
84+
struct icc_node *node;
85+
int ret, i, j;
86+
87+
onecell = devm_kzalloc(dev, struct_size(onecell, nodes, 2 * num_clocks), GFP_KERNEL);
88+
if (!onecell)
89+
return ERR_PTR(-ENOMEM);
90+
91+
qp = devm_kzalloc(dev, struct_size(qp, clocks, num_clocks), GFP_KERNEL);
92+
if (!qp)
93+
return ERR_PTR(-ENOMEM);
94+
95+
qp->num_clocks = num_clocks;
96+
97+
provider = &qp->provider;
98+
provider->dev = dev;
99+
provider->get_bw = icc_clk_get_bw;
100+
provider->set = icc_clk_set;
101+
provider->aggregate = icc_std_aggregate;
102+
provider->xlate = of_icc_xlate_onecell;
103+
INIT_LIST_HEAD(&provider->nodes);
104+
provider->data = onecell;
105+
106+
icc_provider_init(provider);
107+
108+
for (i = 0, j = 0; i < num_clocks; i++) {
109+
qp->clocks[i].clk = data[i].clk;
110+
111+
node = icc_node_create(first_id + j);
112+
if (IS_ERR(node)) {
113+
ret = PTR_ERR(node);
114+
goto err;
115+
}
116+
117+
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_master", data[i].name);
118+
node->data = &qp->clocks[i];
119+
icc_node_add(node, provider);
120+
/* link to the next node, slave */
121+
icc_link_create(node, first_id + j + 1);
122+
onecell->nodes[j++] = node;
123+
124+
node = icc_node_create(first_id + j);
125+
if (IS_ERR(node)) {
126+
ret = PTR_ERR(node);
127+
goto err;
128+
}
129+
130+
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_slave", data[i].name);
131+
/* no data for slave node */
132+
icc_node_add(node, provider);
133+
onecell->nodes[j++] = node;
134+
}
135+
136+
onecell->num_nodes = j;
137+
138+
ret = icc_provider_register(provider);
139+
if (ret)
140+
goto err;
141+
142+
return provider;
143+
144+
err:
145+
icc_nodes_remove(provider);
146+
147+
return ERR_PTR(ret);
148+
}
149+
EXPORT_SYMBOL_GPL(icc_clk_register);
150+
151+
/**
152+
* icc_clk_unregister() - unregister a previously registered clk interconnect provider
153+
* @provider: provider returned by icc_clk_register()
154+
*/
155+
void icc_clk_unregister(struct icc_provider *provider)
156+
{
157+
struct icc_clk_provider *qp = container_of(provider, struct icc_clk_provider, provider);
158+
int i;
159+
160+
icc_provider_deregister(&qp->provider);
161+
icc_nodes_remove(&qp->provider);
162+
163+
for (i = 0; i < qp->num_clocks; i++) {
164+
struct icc_clk_node *qn = &qp->clocks[i];
165+
166+
if (qn->enabled)
167+
clk_disable_unprepare(qn->clk);
168+
}
169+
}
170+
EXPORT_SYMBOL_GPL(icc_clk_unregister);
171+
172+
MODULE_LICENSE("GPL");
173+
MODULE_DESCRIPTION("Interconnect wrapper for clocks");
174+
MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");

0 commit comments

Comments
 (0)