blob: e8c0ba8878ed463ab64eb3d14ed3d024c8ae5674 [file] [log] [blame]
AKASHI Takahirodf234cf2023-10-16 14:39:44 +09001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * SCMI Power domain driver
4 *
5 * Copyright (C) 2023 Linaro Limited
6 * author: AKASHI Takahiro <takahiro.akashi@linaro.org>
7 */
8
9#include <dm.h>
10#include <malloc.h>
11#include <power-domain.h>
12#include <power-domain-uclass.h>
13#include <scmi_agent.h>
Alice Guof71894b2025-04-28 18:37:25 +080014#include <scmi_agent-uclass.h>
AKASHI Takahirodf234cf2023-10-16 14:39:44 +090015#include <scmi_protocols.h>
16#include <dm/device_compat.h>
17
18/**
19 * struct scmi_pwd_properties
20 * @attributes: Power domain attributes
21 * @name: Name of the domain
22 */
23struct scmi_pwd_properties {
24 u32 attributes;
25 u8 *name; /* not used now */
26};
27
28/**
29 * struct scmi_power_domain_priv
30 * @num_pwdoms: Number of power domains
31 * @prop: Pointer to domain's properties
32 * @stats_addr: Address of statistics memory region
33 * @stats_len: Length of statistics memory region
34 */
35struct scmi_power_domain_priv {
36 int num_pwdoms;
37 struct scmi_pwd_properties *prop;
38 u64 stats_addr;
39 size_t stats_len;
40};
41
42/**
43 * async_is_supported - check asynchronous transition
44 * @attributes: Power domain attributes
45 *
46 * Determine if the power transition can be done asynchronously.
47 *
48 * Return: true if supported, false if not
49 */
50static bool async_is_supported(u32 attributes)
51{
52 if (attributes & SCMI_PWD_ATTR_PSTATE_ASYNC)
53 return true;
54
55 /* TODO: check attributes && SCMI_PWD_ATTR_PSTATE_SYNC */
56 return false;
57}
58
59/**
60 * scmi_power_domain_on - Enable the power domain
61 * @power_domain: Power domain
62 *
63 * Turn on the power domain.
64 *
65 * Return: 0 on success, error code on failure
66 */
67static int scmi_power_domain_on(struct power_domain *power_domain)
68{
69 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev);
70 u32 flags, pstate;
71 int ret;
72
73 if (power_domain->id > priv->num_pwdoms)
74 return -EINVAL;
75
76 if (async_is_supported(priv->prop[power_domain->id].attributes))
77 flags = SCMI_PWD_SET_FLAGS_ASYNC;
78 else
79 flags = 0;
80
81 /* ON */
82 pstate = 0;
83
84 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id,
85 pstate);
86 if (ret) {
87 dev_err(power_domain->dev, "failed to set the state on (%d)\n",
88 ret);
89 return ret;
90 }
91
92 return 0;
93}
94
95/**
96 * scmi_power_domain_off - Disable the power domain
97 * @power_domain: Power domain
98 *
99 * Turn off the power domain.
100 *
101 * Return: 0 on success, error code on failure
102 */
103static int scmi_power_domain_off(struct power_domain *power_domain)
104{
105 struct scmi_power_domain_priv *priv = dev_get_priv(power_domain->dev);
106 u32 flags, pstate;
107 int ret;
108
109 if (power_domain->id > priv->num_pwdoms)
110 return -EINVAL;
111
112 if (async_is_supported(priv->prop[power_domain->id].attributes))
113 flags = SCMI_PWD_SET_FLAGS_ASYNC;
114 else
115 flags = 0;
116
117 /* OFF */
118 pstate = SCMI_PWD_PSTATE_TYPE_LOST;
119
120 ret = scmi_pwd_state_set(power_domain->dev, flags, power_domain->id,
121 pstate);
122 if (ret) {
123 dev_err(power_domain->dev, "failed to set the state off (%d)\n",
124 ret);
125 return ret;
126 }
127
128 return 0;
129}
130
131/**
132 * scmi_power_domain_probe - Probe the power domain
133 * @dev: Power domain device
134 *
135 * Probe the power domain and initialize the properties.
136 *
137 * Return: 0 on success, error code on failure
138 */
139static int scmi_power_domain_probe(struct udevice *dev)
140{
141 struct scmi_power_domain_priv *priv = dev_get_priv(dev);
142 u32 version;
143 int i, ret;
144
145 ret = devm_scmi_of_get_channel(dev);
146 if (ret) {
147 dev_err(dev, "failed to get channel (%d)\n", ret);
148 return ret;
149 }
150
151 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_POWER_DOMAIN,
152 &version);
153
154 ret = scmi_pwd_protocol_attrs(dev, &priv->num_pwdoms, &priv->stats_addr,
155 &priv->stats_len);
156 if (ret) {
157 dev_err(dev, "failed to get protocol attributes (%d)\n", ret);
158 return ret;
159 }
160
161 priv->prop = calloc(sizeof(*priv->prop), priv->num_pwdoms);
162 if (!priv->prop)
163 return -ENOMEM;
164
165 for (i = 0; i < priv->num_pwdoms; i++) {
166 ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes,
167 &priv->prop[i].name);
168 if (ret) {
169 dev_err(dev, "failed to get attributes pwd:%d (%d)\n",
170 i, ret);
171 for (i--; i >= 0; i--)
172 free(priv->prop[i].name);
173 free(priv->prop);
174
175 return ret;
176 }
177 }
178
179 return 0;
180}
181
182struct power_domain_ops scmi_power_domain_ops = {
183 .on = scmi_power_domain_on,
184 .off = scmi_power_domain_off,
185};
186
187U_BOOT_DRIVER(scmi_power_domain) = {
188 .name = "scmi_power_domain",
189 .id = UCLASS_POWER_DOMAIN,
190 .ops = &scmi_power_domain_ops,
191 .probe = scmi_power_domain_probe,
192 .priv_auto = sizeof(struct scmi_power_domain_priv),
193};
Alice Guof71894b2025-04-28 18:37:25 +0800194
195static struct scmi_proto_match match[] = {
196 { .proto_id = SCMI_PROTOCOL_ID_POWER_DOMAIN },
197 { /* Sentinel */ }
198};
199
200U_BOOT_SCMI_PROTO_DRIVER(scmi_power_domain, match);