xref: /optee_os/core/drivers/scmi-msg/clock_generic.c (revision ea6b44f4d2c3799d4aab78d3e66b2512eff3ffa8)
1e80130f6SClément Léger // SPDX-License-Identifier: BSD-2-Clause
2e80130f6SClément Léger /*
3e80130f6SClément Léger  * Copyright (c) 2021, Microchip
4e80130f6SClément Léger  */
5e80130f6SClément Léger 
6e80130f6SClément Léger #include <drivers/clk.h>
7e80130f6SClément Léger #include <drivers/scmi.h>
8e80130f6SClément Léger #include <drivers/scmi-msg.h>
9e80130f6SClément Léger #include <kernel/boot.h>
10e80130f6SClément Léger #include <string.h>
11e80130f6SClément Léger #include <sys/queue.h>
12e80130f6SClément Léger 
13e80130f6SClément Léger #include "clock.h"
14e80130f6SClément Léger 
15e80130f6SClément Léger /**
16e80130f6SClément Léger  * struct scmi_clk - Binds an SCMI channel/clock to a core clk reference
17e80130f6SClément Léger  * @clk:        Core clock reference
18e80130f6SClément Léger  * @channel_id: SCMI server channel handle exposing the clock
19e80130f6SClément Léger  * @scmi_id:    SCMI clock domain ID
20e80130f6SClément Léger  * @enabled:    SCMI clock state
21e80130f6SClément Léger  * @link:       Reference in SCMI server clock list
22e80130f6SClément Léger  */
23e80130f6SClément Léger struct scmi_clk {
24e80130f6SClément Léger 	struct clk *clk;
25e80130f6SClément Léger 	unsigned int channel_id;
26e80130f6SClément Léger 	unsigned int scmi_id;
27e80130f6SClément Léger 	bool enabled;
28e80130f6SClément Léger 	SLIST_ENTRY(scmi_clk) link;
29e80130f6SClément Léger };
30e80130f6SClément Léger 
31e80130f6SClément Léger static bool scmi_clk_init_done;
32e80130f6SClément Léger static SLIST_HEAD(, scmi_clk) scmi_clk_list =
33e80130f6SClément Léger 	SLIST_HEAD_INITIALIZER(scmi_clk_list);
34e80130f6SClément Léger 
plat_scmi_clock_count(unsigned int channel_id)35e80130f6SClément Léger size_t plat_scmi_clock_count(unsigned int channel_id)
36e80130f6SClément Léger {
37e80130f6SClément Léger 	unsigned int count = 0;
38e80130f6SClément Léger 	unsigned int max_id = 0;
39e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
40e80130f6SClément Léger 
41e80130f6SClément Léger 	SLIST_FOREACH(clk, &scmi_clk_list, link) {
42e80130f6SClément Léger 		if (clk->channel_id == channel_id) {
43e80130f6SClément Léger 			count++;
44e80130f6SClément Léger 			max_id = MAX(max_id, clk->scmi_id);
45e80130f6SClément Léger 		}
46e80130f6SClément Léger 	}
47e80130f6SClément Léger 
48e80130f6SClément Léger 	if (!count)
49e80130f6SClément Léger 		return 0;
50e80130f6SClément Léger 
51e80130f6SClément Léger 	/* IDs are starting from 0 so we need to return max_id + 1 for count */
52e80130f6SClément Léger 	return max_id + 1;
53e80130f6SClément Léger }
54e80130f6SClément Léger 
clk_scmi_get_by_id(unsigned int channel_id,unsigned int scmi_id)55e80130f6SClément Léger static struct scmi_clk *clk_scmi_get_by_id(unsigned int channel_id,
56e80130f6SClément Léger 					   unsigned int scmi_id)
57e80130f6SClément Léger {
58e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
59e80130f6SClément Léger 
60e80130f6SClément Léger 	SLIST_FOREACH(clk, &scmi_clk_list, link)
61e80130f6SClément Léger 		if (clk->channel_id == channel_id && clk->scmi_id == scmi_id)
62e80130f6SClément Léger 			return clk;
63e80130f6SClément Léger 
64e80130f6SClément Léger 	return NULL;
65e80130f6SClément Léger }
66e80130f6SClément Léger 
plat_scmi_clock_get_name(unsigned int channel_id,unsigned int scmi_id)67e80130f6SClément Léger const char *plat_scmi_clock_get_name(unsigned int channel_id,
68e80130f6SClément Léger 				     unsigned int scmi_id)
69e80130f6SClément Léger {
70e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
71e80130f6SClément Léger 
72e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
73e80130f6SClément Léger 	if (!clk)
74e80130f6SClément Léger 		return "dummy";
75e80130f6SClément Léger 
76e80130f6SClément Léger 	return clk_get_name(clk->clk);
77e80130f6SClément Léger }
78e80130f6SClément Léger 
plat_scmi_clock_rates_array(unsigned int channel_id,unsigned int scmi_id,size_t start_index,unsigned long * rates,size_t * nb_elts)79e80130f6SClément Léger int32_t plat_scmi_clock_rates_array(unsigned int channel_id,
80e80130f6SClément Léger 				    unsigned int scmi_id,
81e80130f6SClément Léger 				    size_t start_index,
82e80130f6SClément Léger 				    unsigned long *rates,
83e80130f6SClément Léger 				    size_t *nb_elts)
84e80130f6SClément Léger {
85*ea6b44f4STony Han 	TEE_Result res = TEE_ERROR_GENERIC;
86e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
87e80130f6SClément Léger 
88e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
89e80130f6SClément Léger 	if (!clk)
90e80130f6SClément Léger 		return SCMI_DENIED;
91e80130f6SClément Léger 
92*ea6b44f4STony Han 	res = clk_get_rates_array(clk->clk, start_index, rates, nb_elts);
93*ea6b44f4STony Han 	if (res == TEE_SUCCESS)
94e80130f6SClément Léger 		return SCMI_SUCCESS;
95*ea6b44f4STony Han 	else if (res == TEE_ERROR_NOT_SUPPORTED)
96*ea6b44f4STony Han 		return SCMI_NOT_SUPPORTED;
97*ea6b44f4STony Han 	else
98*ea6b44f4STony Han 		return SCMI_GENERIC_ERROR;
99e80130f6SClément Léger }
100e80130f6SClément Léger 
plat_scmi_clock_get_rate(unsigned int channel_id,unsigned int scmi_id)101e80130f6SClément Léger unsigned long plat_scmi_clock_get_rate(unsigned int channel_id,
102e80130f6SClément Léger 				       unsigned int scmi_id)
103e80130f6SClément Léger {
104e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
105e80130f6SClément Léger 
106e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
107e80130f6SClément Léger 	if (!clk)
108e80130f6SClément Léger 		return 0;
109e80130f6SClément Léger 
110e80130f6SClément Léger 	return clk_get_rate(clk->clk);
111e80130f6SClément Léger }
112e80130f6SClément Léger 
plat_scmi_clock_set_rate(unsigned int channel_id,unsigned int scmi_id,unsigned long rate)113e80130f6SClément Léger int32_t plat_scmi_clock_set_rate(unsigned int channel_id,
114e80130f6SClément Léger 				 unsigned int scmi_id,
115e80130f6SClément Léger 				 unsigned long rate)
116e80130f6SClément Léger {
117e80130f6SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
118e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
119e80130f6SClément Léger 
120e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
121e80130f6SClément Léger 	if (!clk)
122e80130f6SClément Léger 		return SCMI_DENIED;
123e80130f6SClément Léger 
124e80130f6SClément Léger 	res = clk_set_rate(clk->clk, rate);
125e80130f6SClément Léger 	if (res)
126e80130f6SClément Léger 		return SCMI_GENERIC_ERROR;
127e80130f6SClément Léger 
128e80130f6SClément Léger 	return SCMI_SUCCESS;
129e80130f6SClément Léger }
130e80130f6SClément Léger 
plat_scmi_clock_get_state(unsigned int channel_id,unsigned int scmi_id)131e80130f6SClément Léger int32_t plat_scmi_clock_get_state(unsigned int channel_id,
132e80130f6SClément Léger 				  unsigned int scmi_id)
133e80130f6SClément Léger {
134e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
135e80130f6SClément Léger 
136e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
137e80130f6SClément Léger 	if (!clk)
138e80130f6SClément Léger 		return false;
139e80130f6SClément Léger 
140e80130f6SClément Léger 	return clk->enabled;
141e80130f6SClément Léger }
142e80130f6SClément Léger 
plat_scmi_clock_set_state(unsigned int channel_id,unsigned int scmi_id,bool enable_not_disable)143e80130f6SClément Léger int32_t plat_scmi_clock_set_state(unsigned int channel_id,
144e80130f6SClément Léger 				  unsigned int scmi_id,
145e80130f6SClément Léger 				  bool enable_not_disable)
146e80130f6SClément Léger {
147e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
148e80130f6SClément Léger 
149e80130f6SClément Léger 	clk = clk_scmi_get_by_id(channel_id, scmi_id);
150e80130f6SClément Léger 	if (!clk) {
151e80130f6SClément Léger 		if (enable_not_disable)
152e80130f6SClément Léger 			return SCMI_DENIED;
153e80130f6SClément Léger 		else
154e80130f6SClément Léger 			return SCMI_SUCCESS;
155e80130f6SClément Léger 	}
156e80130f6SClément Léger 
157e80130f6SClément Léger 	if (enable_not_disable) {
158e80130f6SClément Léger 		if (!clk->enabled) {
159e80130f6SClément Léger 			if (clk_enable(clk->clk))
160e80130f6SClément Léger 				return SCMI_GENERIC_ERROR;
161e80130f6SClément Léger 			clk->enabled = true;
162e80130f6SClément Léger 		}
163e80130f6SClément Léger 	} else {
164e80130f6SClément Léger 		if (clk->enabled) {
165e80130f6SClément Léger 			clk_disable(clk->clk);
166e80130f6SClément Léger 			clk->enabled = false;
167e80130f6SClément Léger 		}
168e80130f6SClément Léger 	}
169e80130f6SClément Léger 
170e80130f6SClément Léger 	return SCMI_SUCCESS;
171e80130f6SClément Léger }
172e80130f6SClément Léger 
clk_check_scmi_id(struct clk * new_clk,unsigned int channel_id,unsigned int scmi_id)173e80130f6SClément Léger static TEE_Result clk_check_scmi_id(struct clk *new_clk,
174e80130f6SClément Léger 				    unsigned int channel_id,
175e80130f6SClément Léger 				    unsigned int scmi_id)
176e80130f6SClément Léger {
177e80130f6SClément Léger 	struct scmi_clk *clk = NULL;
178e80130f6SClément Léger 
179e80130f6SClément Léger 	SLIST_FOREACH(clk, &scmi_clk_list, link) {
180e80130f6SClément Léger 		if (clk->channel_id == channel_id && clk->scmi_id == scmi_id) {
181e80130f6SClément Léger 			EMSG("SCMI channel %u, clock %u already registered",
182e80130f6SClément Léger 			     channel_id, scmi_id);
183e80130f6SClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
184e80130f6SClément Léger 		}
185e80130f6SClément Léger 	}
186e80130f6SClément Léger 
187e80130f6SClément Léger 	if (strlen(clk_get_name(new_clk)) >= SCMI_CLOCK_NAME_LENGTH_MAX)
188e80130f6SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
189e80130f6SClément Léger 
190e80130f6SClément Léger 	return TEE_SUCCESS;
191e80130f6SClément Léger }
192e80130f6SClément Léger 
scmi_clk_add(struct clk * clk,unsigned int channel_id,unsigned int scmi_id)193e80130f6SClément Léger TEE_Result scmi_clk_add(struct clk *clk, unsigned int channel_id,
194e80130f6SClément Léger 			unsigned int scmi_id)
195e80130f6SClément Léger {
196e80130f6SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
197e80130f6SClément Léger 	struct scmi_clk *scmi_clk = NULL;
198e80130f6SClément Léger 
199e80130f6SClément Léger 	if (scmi_clk_init_done)
200e80130f6SClément Léger 		return TEE_ERROR_BAD_STATE;
201e80130f6SClément Léger 
202e80130f6SClément Léger 	res = clk_check_scmi_id(clk, channel_id, scmi_id);
203e80130f6SClément Léger 	if (res)
204e80130f6SClément Léger 		return res;
205e80130f6SClément Léger 
206e80130f6SClément Léger 	scmi_clk = calloc(1, sizeof(*scmi_clk));
207e80130f6SClément Léger 	if (!scmi_clk)
208e80130f6SClément Léger 		return TEE_ERROR_OUT_OF_MEMORY;
209e80130f6SClément Léger 
210e80130f6SClément Léger 	scmi_clk->clk = clk;
211e80130f6SClément Léger 	scmi_clk->channel_id = channel_id;
212e80130f6SClément Léger 	scmi_clk->scmi_id = scmi_id;
213e80130f6SClément Léger 	scmi_clk->enabled = false;
214e80130f6SClément Léger 
215e80130f6SClément Léger 	SLIST_INSERT_HEAD(&scmi_clk_list, scmi_clk, link);
216e80130f6SClément Léger 
217e80130f6SClément Léger 	return TEE_SUCCESS;
218e80130f6SClément Léger }
219e80130f6SClément Léger 
scmi_clk_init_fini(void)220e80130f6SClément Léger static TEE_Result scmi_clk_init_fini(void)
221e80130f6SClément Léger {
222e80130f6SClément Léger 	scmi_clk_init_done = true;
223e80130f6SClément Léger 
224e80130f6SClément Léger 	return TEE_SUCCESS;
225e80130f6SClément Léger }
226e80130f6SClément Léger 
227e80130f6SClément Léger release_init_resource(scmi_clk_init_fini);
228