1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/firmware/scmi/clk.h>
8 #include <string.h>
9 
10 /* TODO: if extended attributes are supported this should be moved
11  * to the header file so that users will have access to it.
12  */
13 #define SCMI_CLK_CONFIG_EA_MASK GENMASK(23, 16)
14 
15 struct scmi_clock_attributes_reply {
16 	int32_t status;
17 	uint32_t attributes;
18 };
19 
20 struct scmi_clock_rate_set_reply {
21 	int32_t status;
22 	uint32_t rate[2];
23 };
24 
25 struct scmi_clock_parent_get_reply {
26 	int32_t status;
27 	uint32_t parent_id;
28 };
29 
30 struct scmi_clock_parent_config {
31 	uint32_t clk_id;
32 	uint32_t parent_id;
33 };
34 
scmi_clock_rate_get(struct scmi_protocol * proto,uint32_t clk_id,uint32_t * rate)35 int scmi_clock_rate_get(struct scmi_protocol *proto,
36 			uint32_t clk_id, uint32_t *rate)
37 {
38 	struct scmi_message msg, reply;
39 	int ret;
40 	struct scmi_clock_rate_set_reply reply_buffer;
41 
42 	/* sanity checks */
43 	if (!proto || !rate) {
44 		return -EINVAL;
45 	}
46 
47 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
48 		return -EINVAL;
49 	}
50 
51 	msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_RATE_GET,
52 					SCMI_COMMAND, proto->id, 0x0);
53 	msg.len = sizeof(clk_id);
54 	msg.content = &clk_id;
55 
56 	reply.hdr = msg.hdr;
57 	reply.len = sizeof(reply_buffer);
58 	reply.content = &reply_buffer;
59 
60 	ret = scmi_send_message(proto, &msg, &reply);
61 	if (ret < 0) {
62 		return ret;
63 	}
64 
65 	if (reply_buffer.status != SCMI_SUCCESS) {
66 		return scmi_status_to_errno(reply_buffer.status);
67 	}
68 
69 	*rate = reply_buffer.rate[0];
70 
71 	return 0;
72 }
73 
scmi_clock_rate_set(struct scmi_protocol * proto,struct scmi_clock_rate_config * cfg)74 int scmi_clock_rate_set(struct scmi_protocol *proto, struct scmi_clock_rate_config *cfg)
75 {
76 	struct scmi_message msg, reply;
77 	int status, ret;
78 
79 	/* sanity checks */
80 	if (!proto || !cfg) {
81 		return -EINVAL;
82 	}
83 
84 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
85 		return -EINVAL;
86 	}
87 
88 	/* Currently ASYNC flag is not supported. */
89 	if (cfg->flags & SCMI_CLK_RATE_SET_FLAGS_ASYNC) {
90 		return -ENOTSUP;
91 	}
92 
93 	msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_RATE_SET, SCMI_COMMAND, proto->id, 0x0);
94 	msg.len = sizeof(*cfg);
95 	msg.content = cfg;
96 
97 	reply.hdr = msg.hdr;
98 	reply.len = sizeof(status);
99 	reply.content = &status;
100 
101 	ret = scmi_send_message(proto, &msg, &reply);
102 	if (ret < 0) {
103 		return ret;
104 	}
105 
106 	if (status != SCMI_SUCCESS) {
107 		return scmi_status_to_errno(status);
108 	}
109 
110 	return 0;
111 }
112 
scmi_clock_parent_get(struct scmi_protocol * proto,uint32_t clk_id,uint32_t * parent_id)113 int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t *parent_id)
114 {
115 	struct scmi_message msg, reply;
116 	int ret;
117 	struct scmi_clock_parent_get_reply reply_buffer;
118 
119 	/* sanity checks */
120 	if (!proto || !parent_id) {
121 		return -EINVAL;
122 	}
123 
124 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
125 		return -EINVAL;
126 	}
127 
128 	msg.hdr =
129 		SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_PARENT_GET, SCMI_COMMAND, proto->id, 0x0);
130 	msg.len = sizeof(clk_id);
131 	msg.content = &clk_id;
132 
133 	reply.hdr = msg.hdr;
134 	reply.len = sizeof(reply_buffer);
135 	reply.content = &reply_buffer;
136 
137 	ret = scmi_send_message(proto, &msg, &reply);
138 	if (ret < 0) {
139 		return ret;
140 	}
141 
142 	if (reply_buffer.status != SCMI_SUCCESS) {
143 		return scmi_status_to_errno(reply_buffer.status);
144 	}
145 
146 	*parent_id = reply_buffer.parent_id;
147 
148 	return 0;
149 }
150 
scmi_clock_parent_set(struct scmi_protocol * proto,uint32_t clk_id,uint32_t parent_id)151 int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t parent_id)
152 {
153 	struct scmi_clock_parent_config cfg = {.clk_id = clk_id, .parent_id = parent_id};
154 	struct scmi_message msg, reply;
155 	int status, ret;
156 
157 	/* sanity checks */
158 	if (!proto) {
159 		return -EINVAL;
160 	}
161 
162 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
163 		return -EINVAL;
164 	}
165 
166 	msg.hdr =
167 		SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_PARENT_SET, SCMI_COMMAND, proto->id, 0x0);
168 	msg.len = sizeof(cfg);
169 	msg.content = &cfg;
170 
171 	reply.hdr = msg.hdr;
172 	reply.len = sizeof(status);
173 	reply.content = &status;
174 
175 	ret = scmi_send_message(proto, &msg, &reply);
176 	if (ret < 0) {
177 		return ret;
178 	}
179 
180 	if (status != SCMI_SUCCESS) {
181 		return scmi_status_to_errno(status);
182 	}
183 
184 	return 0;
185 }
186 
scmi_clock_config_set(struct scmi_protocol * proto,struct scmi_clock_config * cfg)187 int scmi_clock_config_set(struct scmi_protocol *proto,
188 			  struct scmi_clock_config *cfg)
189 {
190 	struct scmi_message msg, reply;
191 	int status, ret;
192 
193 	/* sanity checks */
194 	if (!proto || !cfg) {
195 		return -EINVAL;
196 	}
197 
198 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
199 		return -EINVAL;
200 	}
201 
202 	/* extended attributes currently not supported */
203 	if (cfg->attributes & SCMI_CLK_CONFIG_EA_MASK) {
204 		return -ENOTSUP;
205 	}
206 
207 	/* invalid because extended attributes are not supported */
208 	if (SCMI_CLK_CONFIG_ENABLE_DISABLE(cfg->attributes) == 3) {
209 		return -ENOTSUP;
210 	}
211 
212 	/* this is a reserved value */
213 	if (SCMI_CLK_CONFIG_ENABLE_DISABLE(cfg->attributes) == 2) {
214 		return -EINVAL;
215 	}
216 
217 	msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_CONFIG_SET,
218 					SCMI_COMMAND, proto->id, 0x0);
219 	msg.len = sizeof(*cfg);
220 	msg.content = cfg;
221 
222 	reply.hdr = msg.hdr;
223 	reply.len = sizeof(status);
224 	reply.content = &status;
225 
226 	ret = scmi_send_message(proto, &msg, &reply);
227 	if (ret < 0) {
228 		return ret;
229 	}
230 
231 	if (status != SCMI_SUCCESS) {
232 		return scmi_status_to_errno(status);
233 	}
234 
235 	return 0;
236 }
237 
scmi_clock_protocol_attributes(struct scmi_protocol * proto,uint32_t * attributes)238 int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attributes)
239 {
240 	struct scmi_message msg, reply;
241 	struct scmi_clock_attributes_reply reply_buffer;
242 	int ret;
243 
244 	/* sanity checks */
245 	if (!proto || !attributes) {
246 		return -EINVAL;
247 	}
248 
249 	if (proto->id != SCMI_PROTOCOL_CLOCK) {
250 		return -EINVAL;
251 	}
252 
253 	msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_PROTOCOL_ATTRIBUTES,
254 					SCMI_COMMAND, proto->id, 0x0);
255 	/* command has no parameters */
256 	msg.len = 0x0;
257 	msg.content = NULL;
258 
259 	reply.hdr = msg.hdr;
260 	reply.len = sizeof(reply_buffer);
261 	reply.content = &reply_buffer;
262 
263 	ret = scmi_send_message(proto, &msg, &reply);
264 	if (ret < 0) {
265 		return ret;
266 	}
267 
268 	if (reply_buffer.status != 0) {
269 		return scmi_status_to_errno(reply_buffer.status);
270 	}
271 
272 	*attributes = reply_buffer.attributes;
273 
274 	return 0;
275 }
276