firmware: scmi: add power domain protocol support

In this patch, added are helper functions to directly manipulate
SCMI power domain management protocol. DM compliant power domain
driver will be implemented on top of those interfaces in a succeeding
patch.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
This commit is contained in:
AKASHI Takahiro 2023-10-16 14:39:43 +09:00 committed by Tom Rini
parent 04291ee0ab
commit fc358b1a76
3 changed files with 367 additions and 0 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -544,6 +544,184 @@ int scmi_base_set_protocol_permissions(struct udevice *dev,
int scmi_base_reset_agent_configuration(struct udevice *dev, u32 agent_id,
u32 flags);
/*
* SCMI Power Domain Management Protocol
*/
#define SCMI_PWD_PROTOCOL_VERSION 0x30000
#define SCMI_PWD_PSTATE_TYPE_LOST BIT(30)
#define SCMI_PWD_PSTATE_ID GENMASK(27, 0)
enum scmi_power_domain_message_id {
SCMI_PWD_ATTRIBUTES = 0x3,
SCMI_PWD_STATE_SET = 0x4,
SCMI_PWD_STATE_GET = 0x5,
SCMI_PWD_STATE_NOTIFY = 0x6,
SCMI_PWD_STATE_CHANGE_REQUESTED_NOTIFY = 0x7,
SCMI_PWD_NAME_GET = 0x8,
};
/**
* struct scmi_pwd_protocol_attrs_out
* @status: SCMI command status
* @attributes: Protocol attributes
* @stats_addr_low: Lower 32 bits of address of statistics memory region
* @stats_addr_high: Higher 32 bits of address of statistics memory region
* @stats_len: Length of statistics memory region
*/
struct scmi_pwd_protocol_attrs_out {
s32 status;
u32 attributes;
u32 stats_addr_low;
u32 stats_addr_high;
u32 stats_len;
};
#define SCMI_PWD_PROTO_ATTRS_NUM_PWD(attributes) ((attributes) & GENMASK(15, 0))
/**
* struct scmi_pwd_protocol_msg_attrs_out
* @status: SCMI command status
* @attributes: Message-specific attributes
*/
struct scmi_pwd_protocol_msg_attrs_out {
s32 status;
u32 attributes;
};
#define SCMI_PWD_NAME_LENGTH_MAX 16
/**
* struct scmi_pwd_attrs_out
* @status: SCMI command status
* @attributes: Power domain attributes
* @name: Name of power domain
*/
struct scmi_pwd_attrs_out {
s32 status;
u32 attributes;
u8 name[SCMI_PWD_NAME_LENGTH_MAX];
};
#define SCMI_PWD_ATTR_PSTATE_CHANGE_NOTIFY BIT(31)
#define SCMI_PWD_ATTR_PSTATE_ASYNC BIT(30)
#define SCMI_PWD_ATTR_PSTATE_SYNC BIT(29)
#define SCMI_PWD_ATTR_PSTATE_CHANGE_RQ_NOTIFY BIT(28)
#define SCMI_PWD_ATTR_EXTENDED_NAME BIT(27)
/**
* struct scmi_pwd_state_set_in
* @flags: Flags
* @domain_id: Identifier of power domain
* @pstate: Power state of the domain
*/
struct scmi_pwd_state_set_in {
u32 flags;
u32 domain_id;
u32 pstate;
};
#define SCMI_PWD_SET_FLAGS_ASYNC BIT(0)
/**
* struct scmi_pwd_state_get_out
* @status: SCMI command status
* @pstate: Power state of the domain
*/
struct scmi_pwd_state_get_out {
s32 status;
u32 pstate;
};
#define SCMI_PWD_EXTENDED_NAME_MAX 64
/**
* struct scmi_pwd_name_get_out
* @status: SCMI command status
* @flags: Parameter flags
* @extended_name: Extended name of power domain
*/
struct scmi_pwd_name_get_out {
s32 status;
u32 flags;
u8 extended_name[SCMI_PWD_EXTENDED_NAME_MAX];
};
/**
* scmi_pwd_protocol_attrs - get protocol attributes
* @dev: SCMI protocol device
* @num_pwdoms: Number of power domains
* @stats_addr: Address of statistics memory region
* @stats_len: Length of statistics memory region
*
* Obtain the protocol attributes, the number of power domains and
* the information of statistics memory region.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_protocol_attrs(struct udevice *dev, int *num_pwdoms,
u64 *stats_addr, size_t *stats_len);
/**
* scmi_pwd_protocol_message_attrs - get message-specific attributes
* @dev: SCMI protocol device
* @message_id: SCMI message ID
* @attributes: Message-specific attributes
*
* Obtain the message-specific attributes in @attributes.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_protocol_message_attrs(struct udevice *dev, s32 message_id,
u32 *attributes);
/**
* scmi_pwd_attrs - get power domain attributes
* @dev: SCMI protocol device
* @domain_id: Identifier of power domain
* @attributes: Power domain attributes
* @name: Name of power domain
*
* Obtain the attributes of the given power domain, @domain_id, in @attributes
* as well as its name in @name.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_attrs(struct udevice *dev, u32 message_id, u32 *attributes,
u8 **name);
/**
* scmi_pwd_state_set - set power state
* @dev: SCMI protocol device
* @flags: Parameter flags
* @domain_id: Identifier of power domain
* @pstate: Power state
*
* Change the power state of the given power domain, @domain_id.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_state_set(struct udevice *dev, u32 flags, u32 domain_id,
u32 pstate);
/**
* scmi_pwd_state_get - get power state
* @dev: SCMI protocol device
* @domain_id: Identifier of power domain
* @pstate: Power state
*
* Obtain the power state of the given power domain, @domain_id.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_state_get(struct udevice *dev, u32 domain_id, u32 *pstate);
/**
* scmi_pwd_name_get - get extended name
* @dev: SCMI protocol device
* @domain_id: Identifier of power domain
* @name: Extended name of the domain
*
* Obtain the extended name of the given power domain, @domain_id, in @name.
*
* Return: 0 on success, error code on failure
*/
int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name);
/*
* SCMI Clock Protocol
*/