File Index
Files and directories
Sign in
FILE

voltage.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/arm_scmi/voltage.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * System Control and Management Interface (SCMI) Voltage Protocol
  4 *
  5 * Copyright (C) 2020-2022 ARM Ltd.
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/scmi_protocol.h>
 10
 11#include "protocols.h"
 12
 13/* Updated only after ALL the mandatory features for that version are merged */
 14#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x20001
 15
 16#define VOLTAGE_DOMS_NUM_MASK		GENMASK(15, 0)
 17#define REMAINING_LEVELS_MASK		GENMASK(31, 16)
 18#define RETURNED_LEVELS_MASK		GENMASK(11, 0)
 19
 20enum scmi_voltage_protocol_cmd {
 21	VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
 22	VOLTAGE_DESCRIBE_LEVELS = 0x4,
 23	VOLTAGE_CONFIG_SET = 0x5,
 24	VOLTAGE_CONFIG_GET = 0x6,
 25	VOLTAGE_LEVEL_SET = 0x7,
 26	VOLTAGE_LEVEL_GET = 0x8,
 27	VOLTAGE_DOMAIN_NAME_GET = 0x09,
 28};
 29
 30#define NUM_VOLTAGE_DOMAINS(x)	((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
 31
 32struct scmi_msg_resp_domain_attributes {
 33	__le32 attr;
 34#define SUPPORTS_ASYNC_LEVEL_SET(x)	((x) & BIT(31))
 35#define SUPPORTS_EXTENDED_NAMES(x)	((x) & BIT(30))
 36	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
 37};
 38
 39struct scmi_msg_cmd_describe_levels {
 40	__le32 domain_id;
 41	__le32 level_index;
 42};
 43
 44struct scmi_msg_resp_describe_levels {
 45	__le32 flags;
 46#define NUM_REMAINING_LEVELS(f)	((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
 47#define NUM_RETURNED_LEVELS(f)	((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
 48#define SUPPORTS_SEGMENTED_LEVELS(f)	((f) & BIT(12))
 49	__le32 voltage[];
 50};
 51
 52struct scmi_msg_cmd_config_set {
 53	__le32 domain_id;
 54	__le32 config;
 55};
 56
 57struct scmi_msg_cmd_level_set {
 58	__le32 domain_id;
 59	__le32 flags;
 60	__le32 voltage_level;
 61};
 62
 63struct scmi_resp_voltage_level_set_complete {
 64	__le32 domain_id;
 65	__le32 voltage_level;
 66};
 67
 68struct voltage_info {
 69	unsigned int version;
 70	unsigned int num_domains;
 71	struct scmi_voltage_info *domains;
 72};
 73
 74static int scmi_protocol_attributes_get(const struct scmi_protocol_handle *ph,
 75					struct voltage_info *vinfo)
 76{
 77	int ret;
 78	struct scmi_xfer *t;
 79
 80	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
 81				      sizeof(__le32), &t);
 82	if (ret)
 83		return ret;
 84
 85	ret = ph->xops->do_xfer(ph, t);
 86	if (!ret)
 87		vinfo->num_domains =
 88			NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
 89
 90	ph->xops->xfer_put(ph, t);
 91	return ret;
 92}
 93
 94static int scmi_init_voltage_levels(struct device *dev,
 95				    struct scmi_voltage_info *v,
 96				    u32 num_returned, u32 num_remaining,
 97				    bool segmented)
 98{
 99	u32 num_levels;
100
101	num_levels = num_returned + num_remaining;
102	/*
103	 * segmented levels entries are represented by a single triplet
104	 * returned all in one go.
105	 */
106	if (!num_levels ||
107	    (segmented && (num_remaining || num_returned != 3))) {
108		dev_err(dev,
109			"Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
110			num_levels, num_returned, num_remaining, v->id);
111		return -EINVAL;
112	}
113
114	v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
115	if (!v->levels_uv)
116		return -ENOMEM;
117
118	v->num_levels = num_levels;
119	v->segmented = segmented;
120
121	return 0;
122}
123
124struct scmi_volt_ipriv {
125	struct device *dev;
126	struct scmi_voltage_info *v;
127};
128
129static void iter_volt_levels_prepare_message(void *message,
130					     unsigned int desc_index,
131					     const void *priv)
132{
133	struct scmi_msg_cmd_describe_levels *msg = message;
134	const struct scmi_volt_ipriv *p = priv;
135
136	msg->domain_id = cpu_to_le32(p->v->id);
137	msg->level_index = cpu_to_le32(desc_index);
138}
139
140static int iter_volt_levels_update_state(struct scmi_iterator_state *st,
141					 const void *response, void *priv)
142{
143	int ret = 0;
144	u32 flags;
145	const struct scmi_msg_resp_describe_levels *r = response;
146	struct scmi_volt_ipriv *p = priv;
147
148	flags = le32_to_cpu(r->flags);
149	st->num_returned = NUM_RETURNED_LEVELS(flags);
150	st->num_remaining = NUM_REMAINING_LEVELS(flags);
151
152	/* Allocate space for num_levels if not already done */
153	if (!p->v->num_levels) {
154		ret = scmi_init_voltage_levels(p->dev, p->v, st->num_returned,
155					       st->num_remaining,
156					      SUPPORTS_SEGMENTED_LEVELS(flags));
157		if (!ret)
158			st->max_resources = p->v->num_levels;
159	}
160
161	return ret;
162}
163
164static int
165iter_volt_levels_process_response(const struct scmi_protocol_handle *ph,
166				  const void *response,
167				  struct scmi_iterator_state *st, void *priv)
168{
169	s32 val;
170	const struct scmi_msg_resp_describe_levels *r = response;
171	struct scmi_volt_ipriv *p = priv;
172
173	val = (s32)le32_to_cpu(r->voltage[st->loop_idx]);
174	p->v->levels_uv[st->desc_index + st->loop_idx] = val;
175	if (val < 0)
176		p->v->negative_volts_allowed = true;
177
178	return 0;
179}
180
181static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph,
182				   struct scmi_voltage_info *v)
183{
184	int ret;
185	void *iter;
186	struct scmi_iterator_ops ops = {
187		.prepare_message = iter_volt_levels_prepare_message,
188		.update_state = iter_volt_levels_update_state,
189		.process_response = iter_volt_levels_process_response,
190	};
191	struct scmi_volt_ipriv vpriv = {
192		.dev = ph->dev,
193		.v = v,
194	};
195
196	iter = ph->hops->iter_response_init(ph, &ops, v->num_levels,
197					    VOLTAGE_DESCRIBE_LEVELS,
198					    sizeof(struct scmi_msg_cmd_describe_levels),
199					    &vpriv);
200	if (IS_ERR(iter))
201		return PTR_ERR(iter);
202
203	ret = ph->hops->iter_response_run(iter);
204	if (ret) {
205		v->num_levels = 0;
206		devm_kfree(ph->dev, v->levels_uv);
207	}
208
209	return ret;
210}
211
212static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
213					struct voltage_info *vinfo)
214{
215	int ret, dom;
216	struct scmi_xfer *td;
217	struct scmi_msg_resp_domain_attributes *resp_dom;
218
219	ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
220				      sizeof(__le32), sizeof(*resp_dom), &td);
221	if (ret)
222		return ret;
223	resp_dom = td->rx.buf;
224
225	for (dom = 0; dom < vinfo->num_domains; dom++) {
226		u32 attributes;
227		struct scmi_voltage_info *v;
228
229		/* Retrieve domain attributes at first ... */
230		put_unaligned_le32(dom, td->tx.buf);
231		/* Skip domain on comms error */
232		if (ph->xops->do_xfer(ph, td)) {
233			ph->xops->reset_rx_to_maxsz(ph, td);
234			continue;
235		}
236
237		v = vinfo->domains + dom;
238		v->id = dom;
239		attributes = le32_to_cpu(resp_dom->attr);
240		strscpy(v->name, resp_dom->name, SCMI_SHORT_NAME_MAX_SIZE);
241
242		/*
243		 * If supported overwrite short name with the extended one;
244		 * on error just carry on and use already provided short name.
245		 */
246		if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) {
247			if (SUPPORTS_EXTENDED_NAMES(attributes))
248				ph->hops->extended_name_get(ph,
249							VOLTAGE_DOMAIN_NAME_GET,
250							v->id, NULL, v->name,
251							SCMI_MAX_STR_SIZE);
252			if (SUPPORTS_ASYNC_LEVEL_SET(attributes))
253				v->async_level_set = true;
254		}
255
256		/* Skip invalid voltage descriptors */
257		scmi_voltage_levels_get(ph, v);
258	}
259
260	ph->xops->xfer_put(ph, td);
261
262	return ret;
263}
264
265static int __scmi_voltage_get_u32(const struct scmi_protocol_handle *ph,
266				  u8 cmd_id, u32 domain_id, u32 *value)
267{
268	int ret;
269	struct scmi_xfer *t;
270	struct voltage_info *vinfo = ph->get_priv(ph);
271
272	if (domain_id >= vinfo->num_domains)
273		return -EINVAL;
274
275	ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(__le32), 0, &t);
276	if (ret)
277		return ret;
278
279	put_unaligned_le32(domain_id, t->tx.buf);
280	ret = ph->xops->do_xfer(ph, t);
281	if (!ret)
282		*value = get_unaligned_le32(t->rx.buf);
283
284	ph->xops->xfer_put(ph, t);
285	return ret;
286}
287
288static int scmi_voltage_config_set(const struct scmi_protocol_handle *ph,
289				   u32 domain_id, u32 config)
290{
291	int ret;
292	struct scmi_xfer *t;
293	struct voltage_info *vinfo = ph->get_priv(ph);
294	struct scmi_msg_cmd_config_set *cmd;
295
296	if (domain_id >= vinfo->num_domains)
297		return -EINVAL;
298
299	ret = ph->xops->xfer_get_init(ph, VOLTAGE_CONFIG_SET,
300				     sizeof(*cmd), 0, &t);
301	if (ret)
302		return ret;
303
304	cmd = t->tx.buf;
305	cmd->domain_id = cpu_to_le32(domain_id);
306	cmd->config = cpu_to_le32(config & GENMASK(3, 0));
307
308	ret = ph->xops->do_xfer(ph, t);
309
310	ph->xops->xfer_put(ph, t);
311	return ret;
312}
313
314static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
315				   u32 domain_id, u32 *config)
316{
317	return __scmi_voltage_get_u32(ph, VOLTAGE_CONFIG_GET,
318				      domain_id, config);
319}
320
321static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
322				  u32 domain_id,
323				  enum scmi_voltage_level_mode mode,
324				  s32 volt_uV)
325{
326	int ret;
327	struct scmi_xfer *t;
328	struct voltage_info *vinfo = ph->get_priv(ph);
329	struct scmi_msg_cmd_level_set *cmd;
330	struct scmi_voltage_info *v;
331
332	if (domain_id >= vinfo->num_domains)
333		return -EINVAL;
334
335	ret = ph->xops->xfer_get_init(ph, VOLTAGE_LEVEL_SET,
336				      sizeof(*cmd), 0, &t);
337	if (ret)
338		return ret;
339
340	v = vinfo->domains + domain_id;
341
342	cmd = t->tx.buf;
343	cmd->domain_id = cpu_to_le32(domain_id);
344	cmd->voltage_level = cpu_to_le32(volt_uV);
345
346	if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) {
347		cmd->flags = cpu_to_le32(0x0);
348		ret = ph->xops->do_xfer(ph, t);
349	} else {
350		cmd->flags = cpu_to_le32(0x1);
351		ret = ph->xops->do_xfer_with_response(ph, t);
352		if (!ret) {
353			struct scmi_resp_voltage_level_set_complete *resp;
354
355			resp = t->rx.buf;
356			if (le32_to_cpu(resp->domain_id) == domain_id)
357				dev_dbg(ph->dev,
358					"Voltage domain %d set async to %d\n",
359					v->id,
360					le32_to_cpu(resp->voltage_level));
361			else
362				ret = -EPROTO;
363		}
364	}
365
366	ph->xops->xfer_put(ph, t);
367	return ret;
368}
369
370static int scmi_voltage_level_get(const struct scmi_protocol_handle *ph,
371				  u32 domain_id, s32 *volt_uV)
372{
373	return __scmi_voltage_get_u32(ph, VOLTAGE_LEVEL_GET,
374				      domain_id, (u32 *)volt_uV);
375}
376
377static const struct scmi_voltage_info * __must_check
378scmi_voltage_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
379{
380	struct voltage_info *vinfo = ph->get_priv(ph);
381
382	if (domain_id >= vinfo->num_domains ||
383	    !vinfo->domains[domain_id].num_levels)
384		return NULL;
385
386	return vinfo->domains + domain_id;
387}
388
389static int scmi_voltage_domains_num_get(const struct scmi_protocol_handle *ph)
390{
391	struct voltage_info *vinfo = ph->get_priv(ph);
392
393	return vinfo->num_domains;
394}
395
396static const struct scmi_voltage_proto_ops voltage_proto_ops = {
397	.num_domains_get = scmi_voltage_domains_num_get,
398	.info_get = scmi_voltage_info_get,
399	.config_set = scmi_voltage_config_set,
400	.config_get = scmi_voltage_config_get,
401	.level_set = scmi_voltage_level_set,
402	.level_get = scmi_voltage_level_get,
403};
404
405static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
406{
407	int ret;
408	u32 version;
409	struct voltage_info *vinfo;
410
411	ret = ph->xops->version_get(ph, &version);
412	if (ret)
413		return ret;
414
415	dev_dbg(ph->dev, "Voltage Version %d.%d\n",
416		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
417
418	vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL);
419	if (!vinfo)
420		return -ENOMEM;
421	vinfo->version = version;
422
423	ret = scmi_protocol_attributes_get(ph, vinfo);
424	if (ret)
425		return ret;
426
427	if (vinfo->num_domains) {
428		vinfo->domains = devm_kcalloc(ph->dev, vinfo->num_domains,
429					      sizeof(*vinfo->domains),
430					      GFP_KERNEL);
431		if (!vinfo->domains)
432			return -ENOMEM;
433		ret = scmi_voltage_descriptors_get(ph, vinfo);
434		if (ret)
435			return ret;
436	} else {
437		dev_warn(ph->dev, "No Voltage domains found.\n");
438	}
439
440	return ph->set_priv(ph, vinfo, version);
441}
442
443static const struct scmi_protocol scmi_voltage = {
444	.id = SCMI_PROTOCOL_VOLTAGE,
445	.owner = THIS_MODULE,
446	.instance_init = &scmi_voltage_protocol_init,
447	.ops = &voltage_proto_ops,
448	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
449};
450
451DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(voltage, scmi_voltage)
~/ Read-only file preview.