diff options
Diffstat (limited to 'drivers/power/domain/scmi-power-domain.c')
-rw-r--r-- | drivers/power/domain/scmi-power-domain.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c new file mode 100644 index 00000000000..3cd0f075d95 --- /dev/null +++ b/drivers/power/domain/scmi-power-domain.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SCMI Power domain driver + * + * Copyright (C) 2023 Linaro Limited + * author: AKASHI Takahiro <takahiro.akashi@linaro.org> + */ + +#include <dm.h> +#include <malloc.h> +#include <power-domain.h> +#include <power-domain-uclass.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <dm/device_compat.h> + +/** + * struct scmi_pwd_properties + * @attributes: Power domain attributes + * @name: Name of the domain + */ +struct scmi_pwd_properties { + u32 attributes; + u8 *name; /* not used now */ +}; + +/** + * struct scmi_power_domain_priv + * @num_pwdoms: Number of power domains + * @prop: Pointer to domain's properties + * @stats_addr: Address of statistics memory region + * @stats_len: Length of statistics memory region + */ +struct scmi_power_domain_priv { + int num_pwdoms; + struct scmi_pwd_properties *prop; + u64 stats_addr; + size_t stats_len; +}; + +/** + * async_is_supported - check asynchronous transition + * @attributes: Power domain attributes + * + * Determine if the power transition can be done asynchronously. + * + * Return: true if supported, false if not + */ +static bool async_is_supported(u32 attributes) +{ + if (attributes & SCMI_PWD_ATTR_PSTATE_ASYNC) + return true; + + /* TODO: check attributes && SCMI_PWD_ATTR_PSTATE_SYNC */ + return false; +} + +/** + * scmi_power_domain_on - Enable the power domain + * @power_domain: Power domain + * + * Turn on the power domain. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_on(struct power_domain *power_domain) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); + u32 flags, pstate; + int ret; + + if (power_domain->id > priv->num_pwdoms) + return -EINVAL; + + if (async_is_supported(priv->prop[power_domain->id].attributes)) + flags = SCMI_PWD_SET_FLAGS_ASYNC; + else + flags = 0; + + /* ON */ + pstate = 0; + + ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, + pstate); + if (ret) { + dev_err(power_domain->dev, "failed to set the state on (%d)\n", + ret); + return ret; + } + + return 0; +} + +/** + * scmi_power_domain_off - Disable the power domain + * @power_domain: Power domain + * + * Turn off the power domain. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_off(struct power_domain *power_domain) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev); + u32 flags, pstate; + int ret; + + if (power_domain->id > priv->num_pwdoms) + return -EINVAL; + + if (async_is_supported(priv->prop[power_domain->id].attributes)) + flags = SCMI_PWD_SET_FLAGS_ASYNC; + else + flags = 0; + + /* OFF */ + pstate = SCMI_PWD_PSTATE_TYPE_LOST; + + ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id, + pstate); + if (ret) { + dev_err(power_domain->dev, "failed to set the state off (%d)\n", + ret); + return ret; + } + + return 0; +} + +/** + * scmi_power_domain_probe - Probe the power domain + * @dev: Power domain device + * + * Probe the power domain and initialize the properties. + * + * Return: 0 on success, error code on failure + */ +static int scmi_power_domain_probe(struct udevice *dev) +{ + struct scmi_power_domain_priv *priv = dev_get_priv(dev); + u32 version; + int i, ret; + + ret = devm_scmi_of_get_channel(dev); + if (ret) { + dev_err(dev, "failed to get channel (%d)\n", ret); + return ret; + } + + ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_POWER_DOMAIN, + &version); + + ret = scmi_pwd_protocol_attrs(dev, &priv->num_pwdoms, &priv->stats_addr, + &priv->stats_len); + if (ret) { + dev_err(dev, "failed to get protocol attributes (%d)\n", ret); + return ret; + } + + priv->prop = calloc(sizeof(*priv->prop), priv->num_pwdoms); + if (!priv->prop) + return -ENOMEM; + + for (i = 0; i < priv->num_pwdoms; i++) { + ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes, + &priv->prop[i].name); + if (ret) { + dev_err(dev, "failed to get attributes pwd:%d (%d)\n", + i, ret); + for (i--; i >= 0; i--) + free(priv->prop[i].name); + free(priv->prop); + + return ret; + } + } + + return 0; +} + +struct power_domain_ops scmi_power_domain_ops = { + .on = scmi_power_domain_on, + .off = scmi_power_domain_off, +}; + +U_BOOT_DRIVER(scmi_power_domain) = { + .name = "scmi_power_domain", + .id = UCLASS_POWER_DOMAIN, + .ops = &scmi_power_domain_ops, + .probe = scmi_power_domain_probe, + .priv_auto = sizeof(struct scmi_power_domain_priv), +}; |