1 /* SPDX-License-Identifier: GPL-2.0 */
3 * Copyright (c) 2023, Linaro Ltd.
7 #include <linux/device.h>
8 #include <linux/interconnect-clk.h>
9 #include <linux/interconnect-provider.h>
16 struct icc_clk_provider
{
17 struct icc_provider provider
;
19 struct icc_clk_node clocks
[] __counted_by(num_clocks
);
22 #define to_icc_clk_provider(_provider) \
23 container_of(_provider, struct icc_clk_provider, provider)
25 static int icc_clk_set(struct icc_node
*src
, struct icc_node
*dst
)
27 struct icc_clk_node
*qn
= src
->data
;
35 clk_disable_unprepare(qn
->clk
);
42 ret
= clk_prepare_enable(qn
->clk
);
48 return clk_set_rate(qn
->clk
, icc_units_to_bps(src
->peak_bw
));
51 static int icc_clk_get_bw(struct icc_node
*node
, u32
*avg
, u32
*peak
)
53 struct icc_clk_node
*qn
= node
->data
;
58 *peak
= Bps_to_icc(clk_get_rate(qn
->clk
));
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
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
74 * Return: 0 on success, or an error code otherwise
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
)
81 struct icc_clk_provider
*qp
;
82 struct icc_provider
*provider
;
83 struct icc_onecell_data
*onecell
;
84 struct icc_node
*node
;
87 onecell
= devm_kzalloc(dev
, struct_size(onecell
, nodes
, 2 * num_clocks
), GFP_KERNEL
);
89 return ERR_PTR(-ENOMEM
);
90 onecell
->num_nodes
= 2 * num_clocks
;
92 qp
= devm_kzalloc(dev
, struct_size(qp
, clocks
, num_clocks
), GFP_KERNEL
);
94 return ERR_PTR(-ENOMEM
);
96 qp
->num_clocks
= num_clocks
;
98 provider
= &qp
->provider
;
100 provider
->get_bw
= icc_clk_get_bw
;
101 provider
->set
= icc_clk_set
;
102 provider
->aggregate
= icc_std_aggregate
;
103 provider
->xlate
= of_icc_xlate_onecell
;
104 INIT_LIST_HEAD(&provider
->nodes
);
105 provider
->data
= onecell
;
107 icc_provider_init(provider
);
109 for (i
= 0, j
= 0; i
< num_clocks
; i
++) {
110 qp
->clocks
[i
].clk
= data
[i
].clk
;
112 node
= icc_node_create(first_id
+ data
[i
].master_id
);
118 node
->name
= devm_kasprintf(dev
, GFP_KERNEL
, "%s_master", data
[i
].name
);
119 node
->data
= &qp
->clocks
[i
];
120 icc_node_add(node
, provider
);
121 /* link to the next node, slave */
122 icc_link_create(node
, first_id
+ data
[i
].slave_id
);
123 onecell
->nodes
[j
++] = node
;
125 node
= icc_node_create(first_id
+ data
[i
].slave_id
);
131 node
->name
= devm_kasprintf(dev
, GFP_KERNEL
, "%s_slave", data
[i
].name
);
132 /* no data for slave node */
133 icc_node_add(node
, provider
);
134 onecell
->nodes
[j
++] = node
;
137 ret
= icc_provider_register(provider
);
144 icc_nodes_remove(provider
);
148 EXPORT_SYMBOL_GPL(icc_clk_register
);
150 static void devm_icc_release(void *res
)
152 icc_clk_unregister(res
);
155 int devm_icc_clk_register(struct device
*dev
, unsigned int first_id
,
156 unsigned int num_clocks
, const struct icc_clk_data
*data
)
158 struct icc_provider
*prov
;
160 prov
= icc_clk_register(dev
, first_id
, num_clocks
, data
);
162 return PTR_ERR(prov
);
164 return devm_add_action_or_reset(dev
, devm_icc_release
, prov
);
166 EXPORT_SYMBOL_GPL(devm_icc_clk_register
);
169 * icc_clk_unregister() - unregister a previously registered clk interconnect provider
170 * @provider: provider returned by icc_clk_register()
172 void icc_clk_unregister(struct icc_provider
*provider
)
174 struct icc_clk_provider
*qp
= container_of(provider
, struct icc_clk_provider
, provider
);
177 icc_provider_deregister(&qp
->provider
);
178 icc_nodes_remove(&qp
->provider
);
180 for (i
= 0; i
< qp
->num_clocks
; i
++) {
181 struct icc_clk_node
*qn
= &qp
->clocks
[i
];
184 clk_disable_unprepare(qn
->clk
);
187 EXPORT_SYMBOL_GPL(icc_clk_unregister
);
189 MODULE_LICENSE("GPL");
190 MODULE_DESCRIPTION("Interconnect wrapper for clocks");
191 MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");