Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/arm_scmi/system.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* System Control and Management Interface (SCMI) System Power Protocol
4
*
5
* Copyright (C) 2020-2022 ARM Ltd.
6
*/
7
8
#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
9
10
#include <linux/module.h>
11
#include <linux/scmi_protocol.h>
12
13
#include "protocols.h"
14
#include "notify.h"
15
16
/* Updated only after ALL the mandatory features for that version are merged */
17
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
18
19
#define SCMI_SYSTEM_NUM_SOURCES 1
20
21
enum scmi_system_protocol_cmd {
22
SYSTEM_POWER_STATE_NOTIFY = 0x5,
23
};
24
25
struct scmi_system_power_state_notify {
26
__le32 notify_enable;
27
};
28
29
struct scmi_system_power_state_notifier_payld {
30
__le32 agent_id;
31
__le32 flags;
32
__le32 system_state;
33
__le32 timeout;
34
};
35
36
struct scmi_system_info {
37
u32 version;
38
bool graceful_timeout_supported;
39
bool power_state_notify_cmd;
40
};
41
42
static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph,
43
u8 evt_id, u32 src_id)
44
{
45
struct scmi_system_info *pinfo = ph->get_priv(ph);
46
47
if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER)
48
return false;
49
50
return pinfo->power_state_notify_cmd;
51
}
52
53
static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
54
bool enable)
55
{
56
int ret;
57
struct scmi_xfer *t;
58
struct scmi_system_power_state_notify *notify;
59
60
ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY,
61
sizeof(*notify), 0, &t);
62
if (ret)
63
return ret;
64
65
notify = t->tx.buf;
66
notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
67
68
ret = ph->xops->do_xfer(ph, t);
69
70
ph->xops->xfer_put(ph, t);
71
return ret;
72
}
73
74
static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph,
75
u8 evt_id, u32 src_id, bool enable)
76
{
77
int ret;
78
79
ret = scmi_system_request_notify(ph, enable);
80
if (ret)
81
pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
82
83
return ret;
84
}
85
86
static void *
87
scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph,
88
u8 evt_id, ktime_t timestamp,
89
const void *payld, size_t payld_sz,
90
void *report, u32 *src_id)
91
{
92
size_t expected_sz;
93
const struct scmi_system_power_state_notifier_payld *p = payld;
94
struct scmi_system_power_state_notifier_report *r = report;
95
struct scmi_system_info *pinfo = ph->get_priv(ph);
96
97
expected_sz = pinfo->graceful_timeout_supported ?
98
sizeof(*p) : sizeof(*p) - sizeof(__le32);
99
if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
100
payld_sz != expected_sz)
101
return NULL;
102
103
r->timestamp = timestamp;
104
r->agent_id = le32_to_cpu(p->agent_id);
105
r->flags = le32_to_cpu(p->flags);
106
r->system_state = le32_to_cpu(p->system_state);
107
if (pinfo->graceful_timeout_supported &&
108
r->system_state == SCMI_SYSTEM_SHUTDOWN &&
109
SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags))
110
r->timeout = le32_to_cpu(p->timeout);
111
else
112
r->timeout = 0x00;
113
*src_id = 0;
114
115
return r;
116
}
117
118
static const struct scmi_event system_events[] = {
119
{
120
.id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
121
.max_payld_sz =
122
sizeof(struct scmi_system_power_state_notifier_payld),
123
.max_report_sz =
124
sizeof(struct scmi_system_power_state_notifier_report),
125
},
126
};
127
128
static const struct scmi_event_ops system_event_ops = {
129
.is_notify_supported = scmi_system_notify_supported,
130
.set_notify_enabled = scmi_system_set_notify_enabled,
131
.fill_custom_report = scmi_system_fill_custom_report,
132
};
133
134
static const struct scmi_protocol_events system_protocol_events = {
135
.queue_sz = SCMI_PROTO_QUEUE_SZ,
136
.ops = &system_event_ops,
137
.evts = system_events,
138
.num_events = ARRAY_SIZE(system_events),
139
.num_sources = SCMI_SYSTEM_NUM_SOURCES,
140
};
141
142
static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
143
{
144
int ret;
145
u32 version;
146
struct scmi_system_info *pinfo;
147
148
ret = ph->xops->version_get(ph, &version);
149
if (ret)
150
return ret;
151
152
dev_dbg(ph->dev, "System Power Version %d.%d\n",
153
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
154
155
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
156
if (!pinfo)
157
return -ENOMEM;
158
159
pinfo->version = version;
160
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
161
pinfo->graceful_timeout_supported = true;
162
163
if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
164
pinfo->power_state_notify_cmd = true;
165
166
return ph->set_priv(ph, pinfo, version);
167
}
168
169
static const struct scmi_protocol scmi_system = {
170
.id = SCMI_PROTOCOL_SYSTEM,
171
.owner = THIS_MODULE,
172
.instance_init = &scmi_system_protocol_init,
173
.ops = NULL,
174
.events = &system_protocol_events,
175
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
176
};
177
178
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)
179
180