1 // SPDX-License-Identifier: GPL-2.0+
3 * SCMI Power domain driver
5 * Copyright (C) 2023 Linaro Limited
11 #include <power-domain.h>
12 #include <power-domain-uclass.h>
13 #include <scmi_agent.h>
14 #include <scmi_protocols.h>
15 #include <dm/device_compat.h>
18 * struct scmi_pwd_properties
19 * @attributes: Power domain attributes
20 * @name: Name of the domain
22 struct scmi_pwd_properties {
24 u8 *name; /* not used now */
28 * struct scmi_power_domain_priv
29 * @num_pwdoms: Number of power domains
30 * @prop: Pointer to domain's properties
31 * @stats_addr: Address of statistics memory region
32 * @stats_len: Length of statistics memory region
34 struct scmi_power_domain_priv {
36 struct scmi_pwd_properties *prop;
42 * async_is_supported - check asynchronous transition
43 * @attributes: Power domain attributes
45 * Determine if the power transition can be done asynchronously.
47 * Return: true if supported, false if not
49 static bool async_is_supported(u32 attributes)
51 if (attributes & SCMI_PWD_ATTR_PSTATE_ASYNC)
54 /* TODO: check attributes && SCMI_PWD_ATTR_PSTATE_SYNC */
59 * scmi_power_domain_on - Enable the power domain
60 * @power_domain: Power domain
62 * Turn on the power domain.
64 * Return: 0 on success, error code on failure
66 static int scmi_power_domain_on(struct power_domain *power_domain)
68 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev);
72 if (power_domain->id > priv->num_pwdoms)
75 if (async_is_supported(priv->prop[power_domain->id].attributes))
76 flags = SCMI_PWD_SET_FLAGS_ASYNC;
83 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id,
86 dev_err(power_domain->dev, "failed to set the state on (%d)\n",
95 * scmi_power_domain_off - Disable the power domain
96 * @power_domain: Power domain
98 * Turn off the power domain.
100 * Return: 0 on success, error code on failure
102 static int scmi_power_domain_off(struct power_domain *power_domain)
104 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev);
108 if (power_domain->id > priv->num_pwdoms)
111 if (async_is_supported(priv->prop[power_domain->id].attributes))
112 flags = SCMI_PWD_SET_FLAGS_ASYNC;
117 pstate = SCMI_PWD_PSTATE_TYPE_LOST;
119 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id,
122 dev_err(power_domain->dev, "failed to set the state off (%d)\n",
131 * scmi_power_domain_probe - Probe the power domain
132 * @dev: Power domain device
134 * Probe the power domain and initialize the properties.
136 * Return: 0 on success, error code on failure
138 static int scmi_power_domain_probe(struct udevice *dev)
140 struct scmi_power_domain_priv *priv = dev_get_priv(dev);
144 ret = devm_scmi_of_get_channel(dev);
146 dev_err(dev, "failed to get channel (%d)\n", ret);
150 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_POWER_DOMAIN,
153 ret = scmi_pwd_protocol_attrs(dev, &priv->num_pwdoms, &priv->stats_addr,
156 dev_err(dev, "failed to get protocol attributes (%d)\n", ret);
160 priv->prop = calloc(sizeof(*priv->prop), priv->num_pwdoms);
164 for (i = 0; i < priv->num_pwdoms; i++) {
165 ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes,
166 &priv->prop[i].name);
168 dev_err(dev, "failed to get attributes pwd:%d (%d)\n",
170 for (i--; i >= 0; i--)
171 free(priv->prop[i].name);
181 struct power_domain_ops scmi_power_domain_ops = {
182 .on = scmi_power_domain_on,
183 .off = scmi_power_domain_off,
186 U_BOOT_DRIVER(scmi_power_domain) = {
187 .name = "scmi_power_domain",
188 .id = UCLASS_POWER_DOMAIN,
189 .ops = &scmi_power_domain_ops,
190 .probe = scmi_power_domain_probe,
191 .priv_auto = sizeof(struct scmi_power_domain_priv),