Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: arm_scmi: Add SCMI v3.1 VOLTAGE_LEVEL_SET_COMPLETE

Add SCMI v3.1 voltage protocol support for asynchronous VOLTAGE_LEVEL_SET
command.

Note that, if a voltage domain is advertised to support the asynchronous
version of VOLTAGE_LEVEL_SET, the command will be issued asynchronously
unless explicitly requested to use the synchronous version by setting the
mode to SCMI_VOLTAGE_LEVEL_SET_SYNC when calling voltage_ops->level_set.

The SCMI regulator driver level_set invocation has been left unchanged
so that it will transparently use the asynchronous version if available.

Link: https://lore.kernel.org/r/20220330150551.2573938-21-cristian.marussi@arm.com
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

authored by

Cristian Marussi and committed by
Sudeep Holla
4c74701b 7aa75496

+51 -12
+42 -9
drivers/firmware/arm_scmi/voltage.c
··· 28 28 29 29 struct scmi_msg_resp_domain_attributes { 30 30 __le32 attr; 31 + #define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31)) 31 32 #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30)) 32 33 u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 33 34 }; ··· 54 53 struct scmi_msg_cmd_level_set { 55 54 __le32 domain_id; 56 55 __le32 flags; 56 + __le32 voltage_level; 57 + }; 58 + 59 + struct scmi_resp_voltage_level_set_complete { 60 + __le32 domain_id; 57 61 __le32 voltage_level; 58 62 }; 59 63 ··· 220 214 resp_dom = td->rx.buf; 221 215 222 216 for (dom = 0; dom < vinfo->num_domains; dom++) { 217 + u32 attributes; 223 218 struct scmi_voltage_info *v; 224 219 225 220 /* Retrieve domain attributes at first ... */ ··· 232 225 233 226 v = vinfo->domains + dom; 234 227 v->id = dom; 235 - v->attributes = le32_to_cpu(resp_dom->attr); 228 + attributes = le32_to_cpu(resp_dom->attr); 236 229 strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); 237 230 238 231 /* 239 232 * If supported overwrite short name with the extended one; 240 233 * on error just carry on and use already provided short name. 241 234 */ 242 - if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2 && 243 - SUPPORTS_EXTENDED_NAMES(v->attributes)) 244 - ph->hops->extended_name_get(ph, VOLTAGE_DOMAIN_NAME_GET, 245 - v->id, v->name, 246 - SCMI_MAX_STR_SIZE); 235 + if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) { 236 + if (SUPPORTS_EXTENDED_NAMES(attributes)) 237 + ph->hops->extended_name_get(ph, 238 + VOLTAGE_DOMAIN_NAME_GET, 239 + v->id, v->name, 240 + SCMI_MAX_STR_SIZE); 241 + if (SUPPORTS_ASYNC_LEVEL_SET(attributes)) 242 + v->async_level_set = true; 243 + } 247 244 248 245 ret = scmi_voltage_levels_get(ph, v); 249 246 /* Skip invalid voltage descriptors */ ··· 319 308 } 320 309 321 310 static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, 322 - u32 domain_id, u32 flags, s32 volt_uV) 311 + u32 domain_id, 312 + enum scmi_voltage_level_mode mode, 313 + s32 volt_uV) 323 314 { 324 315 int ret; 325 316 struct scmi_xfer *t; 326 317 struct voltage_info *vinfo = ph->get_priv(ph); 327 318 struct scmi_msg_cmd_level_set *cmd; 319 + struct scmi_voltage_info *v; 328 320 329 321 if (domain_id >= vinfo->num_domains) 330 322 return -EINVAL; ··· 337 323 if (ret) 338 324 return ret; 339 325 326 + v = vinfo->domains + domain_id; 327 + 340 328 cmd = t->tx.buf; 341 329 cmd->domain_id = cpu_to_le32(domain_id); 342 - cmd->flags = cpu_to_le32(flags); 343 330 cmd->voltage_level = cpu_to_le32(volt_uV); 344 331 345 - ret = ph->xops->do_xfer(ph, t); 332 + if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) { 333 + cmd->flags = cpu_to_le32(0x0); 334 + ret = ph->xops->do_xfer(ph, t); 335 + } else { 336 + cmd->flags = cpu_to_le32(0x1); 337 + ret = ph->xops->do_xfer_with_response(ph, t); 338 + if (!ret) { 339 + struct scmi_resp_voltage_level_set_complete *resp; 340 + 341 + resp = t->rx.buf; 342 + if (le32_to_cpu(resp->domain_id) == domain_id) 343 + dev_dbg(ph->dev, 344 + "Voltage domain %d set async to %d\n", 345 + v->id, 346 + le32_to_cpu(resp->voltage_level)); 347 + else 348 + ret = -EPROTO; 349 + } 350 + } 346 351 347 352 ph->xops->xfer_put(ph, t); 348 353 return ret;
+9 -3
include/linux/scmi_protocol.h
··· 495 495 int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain); 496 496 }; 497 497 498 + enum scmi_voltage_level_mode { 499 + SCMI_VOLTAGE_LEVEL_SET_AUTO, 500 + SCMI_VOLTAGE_LEVEL_SET_SYNC, 501 + }; 502 + 498 503 /** 499 504 * struct scmi_voltage_info - describe one available SCMI Voltage Domain 500 505 * ··· 512 507 * supported voltage level 513 508 * @negative_volts_allowed: True if any of the entries of @levels_uv represent 514 509 * a negative voltage. 515 - * @attributes: represents Voltage Domain advertised attributes 510 + * @async_level_set: True when the voltage domain supports asynchronous level 511 + * set commands. 516 512 * @name: name assigned to the Voltage Domain by platform 517 513 * @num_levels: number of total entries in @levels_uv. 518 514 * @levels_uv: array of entries describing the available voltage levels for ··· 523 517 unsigned int id; 524 518 bool segmented; 525 519 bool negative_volts_allowed; 526 - unsigned int attributes; 520 + bool async_level_set; 527 521 char name[SCMI_MAX_STR_SIZE]; 528 522 unsigned int num_levels; 529 523 #define SCMI_VOLTAGE_SEGMENT_LOW 0 ··· 554 548 int (*config_get)(const struct scmi_protocol_handle *ph, u32 domain_id, 555 549 u32 *config); 556 550 int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain_id, 557 - u32 flags, s32 volt_uV); 551 + enum scmi_voltage_level_mode mode, s32 volt_uV); 558 552 int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain_id, 559 553 s32 *volt_uV); 560 554 };