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