aboutsummaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/scmi/Makefile1
-rw-r--r--drivers/firmware/scmi/pwdom.c188
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c265
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_devices.c10
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c11
5 files changed, 474 insertions, 1 deletions
diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
index 1a23d49817..dae4286358 100644
--- a/drivers/firmware/scmi/Makefile
+++ b/drivers/firmware/scmi/Makefile
@@ -4,4 +4,5 @@ obj-y += smt.o
obj-$(CONFIG_SCMI_AGENT_SMCCC) += smccc_agent.o
obj-$(CONFIG_SCMI_AGENT_MAILBOX) += mailbox_agent.o
obj-$(CONFIG_SCMI_AGENT_OPTEE) += optee_agent.o
+obj-$(CONFIG_SCMI_POWER_DOMAIN) += pwdom.o
obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o
diff --git a/drivers/firmware/scmi/pwdom.c b/drivers/firmware/scmi/pwdom.c
new file mode 100644
index 0000000000..de2ba4755a
--- /dev/null
+++ b/drivers/firmware/scmi/pwdom.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SCMI Power domain management protocol
+ *
+ * Copyright (C) 2023 Linaro Limited
+ * author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <string.h>
+#include <asm/types.h>
+
+int scmi_pwd_protocol_attrs(struct udevice *dev, int *num_pwdoms,
+ u64 *stats_addr, size_t *stats_len)
+{
+ struct scmi_pwd_protocol_attrs_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PROTOCOL_ATTRIBUTES,
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ if (!dev || !num_pwdoms || !stats_addr || !stats_len)
+ return -EINVAL;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (out.status)
+ return scmi_to_linux_errno(out.status);
+
+ *num_pwdoms = SCMI_PWD_PROTO_ATTRS_NUM_PWD(out.attributes);
+ *stats_addr = ((u64)out.stats_addr_high << 32) + out.stats_addr_low;
+ *stats_len = out.stats_len;
+
+ return 0;
+}
+
+int scmi_pwd_protocol_message_attrs(struct udevice *dev, s32 message_id,
+ u32 *attributes)
+{
+ struct scmi_pwd_protocol_msg_attrs_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PROTOCOL_MESSAGE_ATTRIBUTES,
+ .in_msg = (u8 *)&message_id,
+ .in_msg_sz = sizeof(message_id),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ if (!dev || !attributes)
+ return -EINVAL;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (out.status)
+ return scmi_to_linux_errno(out.status);
+
+ *attributes = out.attributes;
+
+ return 0;
+}
+
+int scmi_pwd_attrs(struct udevice *dev, u32 domain_id, u32 *attributes,
+ u8 **name)
+{
+ struct scmi_pwd_attrs_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PWD_ATTRIBUTES,
+ .in_msg = (u8 *)&domain_id,
+ .in_msg_sz = sizeof(domain_id),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ if (!dev || !attributes || !name)
+ return -EINVAL;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (out.status)
+ return scmi_to_linux_errno(out.status);
+
+ *name = strdup(out.name);
+ if (!*name)
+ return -ENOMEM;
+
+ *attributes = out.attributes;
+
+ return 0;
+}
+
+int scmi_pwd_state_set(struct udevice *dev, u32 flags, u32 domain_id,
+ u32 pstate)
+{
+ struct scmi_pwd_state_set_in in;
+ s32 status;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PWD_STATE_SET,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)&status,
+ .out_msg_sz = sizeof(status),
+ };
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ in.flags = flags;
+ in.domain_id = domain_id;
+ in.pstate = pstate;
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (status)
+ return scmi_to_linux_errno(status);
+
+ return 0;
+}
+
+int scmi_pwd_state_get(struct udevice *dev, u32 domain_id, u32 *pstate)
+{
+ struct scmi_pwd_state_get_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PWD_STATE_GET,
+ .in_msg = (u8 *)&domain_id,
+ .in_msg_sz = sizeof(domain_id),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ if (!dev || !pstate)
+ return -EINVAL;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (out.status)
+ return scmi_to_linux_errno(out.status);
+
+ *pstate = out.pstate;
+
+ return 0;
+}
+
+int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name)
+{
+ struct scmi_pwd_name_get_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ .message_id = SCMI_PWD_NAME_GET,
+ .in_msg = (u8 *)&domain_id,
+ .in_msg_sz = sizeof(domain_id),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+
+ if (!dev || !name)
+ return -EINVAL;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+ if (out.status)
+ return scmi_to_linux_errno(out.status);
+
+ *name = strdup(out.extended_name);
+ if (!*name)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index eb567dd900..9f5f497e0a 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -43,6 +43,8 @@
#define SANDBOX_SCMI_AGENT_NAME "OSPM"
#define SANDBOX_SCMI_PLATFORM_NAME "platform"
+#define SANDBOX_SCMI_PWD_PROTOCOL_VERSION SCMI_PWD_PROTOCOL_VERSION
+
/**
* struct sandbox_channel - Description of sandbox transport
* @channel_id: Channel identifier
@@ -64,6 +66,7 @@ struct scmi_channel {
};
static u8 protocols[] = {
+ SCMI_PROTOCOL_ID_POWER_DOMAIN,
SCMI_PROTOCOL_ID_CLOCK,
SCMI_PROTOCOL_ID_RESET_DOMAIN,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
@@ -71,6 +74,12 @@ static u8 protocols[] = {
#define NUM_PROTOCOLS ARRAY_SIZE(protocols)
+static struct sandbox_scmi_pwd scmi_pwdom[] = {
+ { .id = 0 },
+ { .id = 1 },
+ { .id = 2 },
+};
+
static struct sandbox_scmi_clk scmi_clk[] = {
{ .rate = 333 },
{ .rate = 200 },
@@ -458,6 +467,238 @@ static int sandbox_scmi_base_reset_agent_configuration(struct udevice *dev,
return 0;
}
+/* Power Domain Management Protocol */
+
+/**
+ * sandbox_scmi_pwd_protocol_version - implement SCMI_PROTOCOL_VERSION
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PROTOCOL_VERSION command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_protocol_version(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_protocol_version_out *out = NULL;
+
+ if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ out = (struct scmi_protocol_version_out *)msg->out_msg;
+ out->version = SANDBOX_SCMI_PWD_PROTOCOL_VERSION;
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_protocol_attribs - implement SCMI_PWD_PROTOCOL_ATTRS
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_PROTOCOL_ATTRS command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_protocol_attribs(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_pwd_protocol_attrs_out *out;
+
+ if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ out = (struct scmi_pwd_protocol_attrs_out *)msg->out_msg;
+
+ out->attributes = ARRAY_SIZE(scmi_pwdom);
+ out->stats_addr_low = 0;
+ out->stats_addr_high = 0;
+ out->stats_len = 0;
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_protocol_msg_attribs - implement
+ SCMI_PWD_PROTOCOL_MESSAGE_ATTRS
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_PROTOCOL_MESSAGE_ATTRS command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_protocol_msg_attribs(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ u32 message_id;
+ struct scmi_pwd_protocol_msg_attrs_out *out;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(message_id) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ message_id = *(u32 *)msg->in_msg;
+
+ out = (struct scmi_pwd_protocol_msg_attrs_out *)msg->out_msg;
+ if (message_id <= SCMI_PWD_STATE_GET ||
+ message_id == SCMI_PWD_NAME_GET) {
+ out->attributes = 0;
+ out->status = SCMI_SUCCESS;
+ } else {
+ out->status = SCMI_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_attribs - implement SCMI_PWD_ATTRS
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_ATTRS command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_attribs(struct udevice *dev, struct scmi_msg *msg)
+{
+ u32 domain_id;
+ struct scmi_pwd_attrs_out *out;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ domain_id = *(u32 *)msg->in_msg;
+ out = (struct scmi_pwd_attrs_out *)msg->out_msg;
+
+ if (domain_id > ARRAY_SIZE(scmi_pwdom)) {
+ out->status = SCMI_NOT_FOUND;
+
+ return 0;
+ }
+
+ out->attributes =
+ SCMI_PWD_ATTR_PSTATE_SYNC | SCMI_PWD_ATTR_EXTENDED_NAME;
+ /* just 15-char + NULL */
+ snprintf(out->name, SCMI_PWD_NAME_LENGTH_MAX, "power-domain--%d",
+ domain_id);
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_state_set - implement SCMI_PWD_STATE_SET
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_STATE_SET command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_state_set(struct udevice *dev, struct scmi_msg *msg)
+{
+ struct scmi_pwd_state_set_in *in;
+ s32 *status;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(in) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+ return -EINVAL;
+
+ in = (struct scmi_pwd_state_set_in *)msg->in_msg;
+ status = (s32 *)msg->out_msg;
+
+ if (in->domain_id > ARRAY_SIZE(scmi_pwdom)) {
+ *status = SCMI_NOT_FOUND;
+
+ return 0;
+ }
+
+ if ((in->flags & SCMI_PWD_SET_FLAGS_ASYNC) ||
+ (in->pstate != SCMI_PWD_PSTATE_TYPE_LOST && in->pstate)) {
+ *status = SCMI_INVALID_PARAMETERS;
+
+ return 0;
+ }
+
+ scmi_pwdom[in->domain_id].pstate = in->pstate;
+ *status = SCMI_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_state_get - implement SCMI_PWD_STATE_GET
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_STATE_GET command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_state_get(struct udevice *dev, struct scmi_msg *msg)
+{
+ u32 domain_id;
+ struct scmi_pwd_state_get_out *out;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ domain_id = *(u32 *)msg->in_msg;
+ out = (struct scmi_pwd_state_get_out *)msg->out_msg;
+
+ if (domain_id > ARRAY_SIZE(scmi_pwdom)) {
+ out->status = SCMI_NOT_FOUND;
+
+ return 0;
+ }
+
+ out->pstate = scmi_pwdom[domain_id].pstate;
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
+/**
+ * sandbox_scmi_pwd_name_get - implement SCMI_PWD_NAME_GET
+ * @dev: SCMI device
+ * @msg: SCMI message
+ *
+ * Implement SCMI_PWD_NAME_GET command.
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int sandbox_scmi_pwd_name_get(struct udevice *dev, struct scmi_msg *msg)
+{
+ u32 domain_id;
+ struct scmi_pwd_name_get_out *out;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(domain_id) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ domain_id = *(u32 *)msg->in_msg;
+ out = (struct scmi_pwd_name_get_out *)msg->out_msg;
+
+ if (domain_id > ARRAY_SIZE(scmi_pwdom)) {
+ out->status = SCMI_NOT_FOUND;
+
+ return 0;
+ }
+
+ snprintf(out->extended_name, SCMI_PWD_EXTENDED_NAME_MAX,
+ "power-domain--%d-extended", domain_id);
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
/* Clock Protocol */
static int sandbox_scmi_clock_protocol_attribs(struct udevice *dev,
@@ -918,6 +1159,26 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
break;
}
break;
+ case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+ switch (msg->message_id) {
+ case SCMI_PROTOCOL_VERSION:
+ return sandbox_scmi_pwd_protocol_version(dev, msg);
+ case SCMI_PROTOCOL_ATTRIBUTES:
+ return sandbox_scmi_pwd_protocol_attribs(dev, msg);
+ case SCMI_PROTOCOL_MESSAGE_ATTRIBUTES:
+ return sandbox_scmi_pwd_protocol_msg_attribs(dev, msg);
+ case SCMI_PWD_ATTRIBUTES:
+ return sandbox_scmi_pwd_attribs(dev, msg);
+ case SCMI_PWD_STATE_SET:
+ return sandbox_scmi_pwd_state_set(dev, msg);
+ case SCMI_PWD_STATE_GET:
+ return sandbox_scmi_pwd_state_get(dev, msg);
+ case SCMI_PWD_NAME_GET:
+ return sandbox_scmi_pwd_name_get(dev, msg);
+ default:
+ break;
+ }
+ break;
case SCMI_PROTOCOL_ID_CLOCK:
switch (msg->message_id) {
case SCMI_PROTOCOL_ATTRIBUTES:
@@ -960,7 +1221,6 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
break;
}
break;
- case SCMI_PROTOCOL_ID_POWER_DOMAIN:
case SCMI_PROTOCOL_ID_SYSTEM:
case SCMI_PROTOCOL_ID_PERF:
case SCMI_PROTOCOL_ID_SENSOR:
@@ -993,6 +1253,9 @@ static int sandbox_scmi_test_probe(struct udevice *dev)
struct sandbox_scmi_agent *agent = dev_get_priv(dev);
*agent = (struct sandbox_scmi_agent){
+ .pwdom_version = SANDBOX_SCMI_PWD_PROTOCOL_VERSION,
+ .pwdom = scmi_pwdom,
+ .pwdom_count = ARRAY_SIZE(scmi_pwdom),
.clk = scmi_clk,
.clk_count = ARRAY_SIZE(scmi_clk),
.reset = scmi_reset,
diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c
index 9baeb469ec..facb5b06ff 100644
--- a/drivers/firmware/scmi/sandbox-scmi_devices.c
+++ b/drivers/firmware/scmi/sandbox-scmi_devices.c
@@ -29,12 +29,14 @@
/*
* struct sandbox_scmi_device_priv - Storage for device handles used by test
+ * @pwdom: Power domain device
* @clk: Array of clock instances used by tests
* @reset_clt: Array of the reset controller instances used by tests
* @regulators: Array of regulator device references used by the tests
* @devices: Resources exposed by sandbox_scmi_devices_ctx()
*/
struct sandbox_scmi_device_priv {
+ struct power_domain pwdom;
struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT];
struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT];
struct udevice *regulators[SCMI_TEST_DEVICES_VOLTD_COUNT];
@@ -77,6 +79,8 @@ static int sandbox_scmi_devices_probe(struct udevice *dev)
size_t n;
priv->devices = (struct sandbox_scmi_devices){
+ .pwdom = &priv->pwdom,
+ .pwdom_count = 1,
.clk = priv->clk,
.clk_count = SCMI_TEST_DEVICES_CLK_COUNT,
.reset = priv->reset_ctl,
@@ -85,6 +89,12 @@ static int sandbox_scmi_devices_probe(struct udevice *dev)
.regul_count = SCMI_TEST_DEVICES_VOLTD_COUNT,
};
+ ret = power_domain_get_by_index(dev, priv->devices.pwdom, 0);
+ if (ret) {
+ dev_err(dev, "%s: Failed on power domain\n", __func__);
+ return ret;
+ }
+
for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) {
ret = clk_get_by_index(dev, n, priv->devices.clk + n);
if (ret) {
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index 6f585b96f7..0f1003e167 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -86,6 +86,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_BASE:
proto = priv->base_dev;
break;
+ case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+ proto = priv->pwdom_dev;
+ break;
case SCMI_PROTOCOL_ID_CLOCK:
proto = priv->clock_dev;
break;
@@ -133,6 +136,9 @@ static int scmi_add_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_BASE:
priv->base_dev = proto;
break;
+ case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+ priv->pwdom_dev = proto;
+ break;
case SCMI_PROTOCOL_ID_CLOCK:
priv->clock_dev = proto;
break;
@@ -405,6 +411,11 @@ static int scmi_bind_protocols(struct udevice *dev)
drv = NULL;
name = ofnode_get_name(node);
switch (protocol_id) {
+ case SCMI_PROTOCOL_ID_POWER_DOMAIN:
+ if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN) &&
+ scmi_protocol_is_supported(dev, protocol_id))
+ drv = DM_DRIVER_GET(scmi_power_domain);
+ break;
case SCMI_PROTOCOL_ID_CLOCK:
if (CONFIG_IS_ENABLED(CLK_SCMI) &&
scmi_protocol_is_supported(dev, protocol_id))