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