1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
// 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_agent-uclass.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),
};
static struct scmi_proto_match match[] = {
{ .proto_id = SCMI_PROTOCOL_ID_POWER_DOMAIN },
{ /* Sentinel */ }
};
U_BOOT_SCMI_PROTO_DRIVER(scmi_power_domain, match);
|