1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) Power Protocol 4 * 5 * Copyright (C) 2018-2022 ARM Ltd. 6 */ 7 8 #define pr_fmt(fmt) "SCMI Notifications POWER - " 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 0x30000 18 19 enum scmi_power_protocol_cmd { 20 POWER_DOMAIN_ATTRIBUTES = 0x3, 21 POWER_STATE_SET = 0x4, 22 POWER_STATE_GET = 0x5, 23 POWER_STATE_NOTIFY = 0x6, 24 POWER_DOMAIN_NAME_GET = 0x8, 25 }; 26 27 struct scmi_msg_resp_power_attributes { 28 __le16 num_domains; 29 __le16 reserved; 30 __le32 stats_addr_low; 31 __le32 stats_addr_high; 32 __le32 stats_size; 33 }; 34 35 struct scmi_msg_resp_power_domain_attributes { 36 __le32 flags; 37 #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31)) 38 #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30)) 39 #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29)) 40 #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27)) 41 u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 42 }; 43 44 struct scmi_power_set_state { 45 __le32 flags; 46 #define STATE_SET_ASYNC BIT(0) 47 __le32 domain; 48 __le32 state; 49 }; 50 51 struct scmi_power_state_notify { 52 __le32 domain; 53 __le32 notify_enable; 54 }; 55 56 struct scmi_power_state_notify_payld { 57 __le32 agent_id; 58 __le32 domain_id; 59 __le32 power_state; 60 }; 61 62 struct power_dom_info { 63 bool state_set_sync; 64 bool state_set_async; 65 bool state_set_notify; 66 char name[SCMI_MAX_STR_SIZE]; 67 }; 68 69 struct scmi_power_info { 70 u32 version; 71 bool notify_state_change_cmd; 72 int num_domains; 73 u64 stats_addr; 74 u32 stats_size; 75 struct power_dom_info *dom_info; 76 }; 77 78 static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, 79 struct scmi_power_info *pi) 80 { 81 int ret; 82 struct scmi_xfer *t; 83 struct scmi_msg_resp_power_attributes *attr; 84 85 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 86 0, sizeof(*attr), &t); 87 if (ret) 88 return ret; 89 90 attr = t->rx.buf; 91 92 ret = ph->xops->do_xfer(ph, t); 93 if (!ret) { 94 pi->num_domains = le16_to_cpu(attr->num_domains); 95 pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | 96 (u64)le32_to_cpu(attr->stats_addr_high) << 32; 97 pi->stats_size = le32_to_cpu(attr->stats_size); 98 } 99 100 ph->xops->xfer_put(ph, t); 101 102 if (!ret) 103 if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL)) 104 pi->notify_state_change_cmd = true; 105 106 return ret; 107 } 108 109 static int 110 scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, 111 u32 domain, struct power_dom_info *dom_info, 112 u32 version, bool notify_state_change_cmd) 113 { 114 int ret; 115 u32 flags; 116 struct scmi_xfer *t; 117 struct scmi_msg_resp_power_domain_attributes *attr; 118 119 ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES, 120 sizeof(domain), sizeof(*attr), &t); 121 if (ret) 122 return ret; 123 124 put_unaligned_le32(domain, t->tx.buf); 125 attr = t->rx.buf; 126 127 ret = ph->xops->do_xfer(ph, t); 128 if (!ret) { 129 flags = le32_to_cpu(attr->flags); 130 131 if (notify_state_change_cmd) 132 dom_info->state_set_notify = 133 SUPPORTS_STATE_SET_NOTIFY(flags); 134 dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); 135 dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); 136 strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); 137 } 138 ph->xops->xfer_put(ph, t); 139 140 /* 141 * If supported overwrite short name with the extended one; 142 * on error just carry on and use already provided short name. 143 */ 144 if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && 145 SUPPORTS_EXTENDED_NAMES(flags)) { 146 ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET, 147 domain, NULL, dom_info->name, 148 SCMI_MAX_STR_SIZE); 149 } 150 151 return ret; 152 } 153 154 static int scmi_power_state_set(const struct scmi_protocol_handle *ph, 155 u32 domain, u32 state) 156 { 157 int ret; 158 struct scmi_xfer *t; 159 struct scmi_power_set_state *st; 160 161 ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t); 162 if (ret) 163 return ret; 164 165 st = t->tx.buf; 166 st->flags = cpu_to_le32(0); 167 st->domain = cpu_to_le32(domain); 168 st->state = cpu_to_le32(state); 169 170 ret = ph->xops->do_xfer(ph, t); 171 172 ph->xops->xfer_put(ph, t); 173 return ret; 174 } 175 176 static int scmi_power_state_get(const struct scmi_protocol_handle *ph, 177 u32 domain, u32 *state) 178 { 179 int ret; 180 struct scmi_xfer *t; 181 182 ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t); 183 if (ret) 184 return ret; 185 186 put_unaligned_le32(domain, t->tx.buf); 187 188 ret = ph->xops->do_xfer(ph, t); 189 if (!ret) 190 *state = get_unaligned_le32(t->rx.buf); 191 192 ph->xops->xfer_put(ph, t); 193 return ret; 194 } 195 196 static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph) 197 { 198 struct scmi_power_info *pi = ph->get_priv(ph); 199 200 return pi->num_domains; 201 } 202 203 static const char * 204 scmi_power_name_get(const struct scmi_protocol_handle *ph, 205 u32 domain) 206 { 207 struct scmi_power_info *pi = ph->get_priv(ph); 208 struct power_dom_info *dom = pi->dom_info + domain; 209 210 return dom->name; 211 } 212 213 static const struct scmi_power_proto_ops power_proto_ops = { 214 .num_domains_get = scmi_power_num_domains_get, 215 .name_get = scmi_power_name_get, 216 .state_set = scmi_power_state_set, 217 .state_get = scmi_power_state_get, 218 }; 219 220 static int scmi_power_request_notify(const struct scmi_protocol_handle *ph, 221 u32 domain, bool enable) 222 { 223 int ret; 224 struct scmi_xfer *t; 225 struct scmi_power_state_notify *notify; 226 227 ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY, 228 sizeof(*notify), 0, &t); 229 if (ret) 230 return ret; 231 232 notify = t->tx.buf; 233 notify->domain = cpu_to_le32(domain); 234 notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; 235 236 ret = ph->xops->do_xfer(ph, t); 237 238 ph->xops->xfer_put(ph, t); 239 return ret; 240 } 241 242 static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph, 243 u8 evt_id, u32 src_id) 244 { 245 struct power_dom_info *dom; 246 struct scmi_power_info *pinfo = ph->get_priv(ph); 247 248 if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || 249 src_id >= pinfo->num_domains) 250 return false; 251 252 dom = pinfo->dom_info + src_id; 253 return dom->state_set_notify; 254 } 255 256 static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph, 257 u8 evt_id, u32 src_id, bool enable) 258 { 259 int ret; 260 261 ret = scmi_power_request_notify(ph, src_id, enable); 262 if (ret) 263 pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n", 264 evt_id, src_id, ret); 265 266 return ret; 267 } 268 269 static void * 270 scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph, 271 u8 evt_id, ktime_t timestamp, 272 const void *payld, size_t payld_sz, 273 void *report, u32 *src_id) 274 { 275 const struct scmi_power_state_notify_payld *p = payld; 276 struct scmi_power_state_changed_report *r = report; 277 278 if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz) 279 return NULL; 280 281 r->timestamp = timestamp; 282 r->agent_id = le32_to_cpu(p->agent_id); 283 r->domain_id = le32_to_cpu(p->domain_id); 284 r->power_state = le32_to_cpu(p->power_state); 285 *src_id = r->domain_id; 286 287 return r; 288 } 289 290 static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph) 291 { 292 struct scmi_power_info *pinfo = ph->get_priv(ph); 293 294 if (!pinfo) 295 return -EINVAL; 296 297 return pinfo->num_domains; 298 } 299 300 static const struct scmi_event power_events[] = { 301 { 302 .id = SCMI_EVENT_POWER_STATE_CHANGED, 303 .max_payld_sz = sizeof(struct scmi_power_state_notify_payld), 304 .max_report_sz = 305 sizeof(struct scmi_power_state_changed_report), 306 }, 307 }; 308 309 static const struct scmi_event_ops power_event_ops = { 310 .is_notify_supported = scmi_power_notify_supported, 311 .get_num_sources = scmi_power_get_num_sources, 312 .set_notify_enabled = scmi_power_set_notify_enabled, 313 .fill_custom_report = scmi_power_fill_custom_report, 314 }; 315 316 static const struct scmi_protocol_events power_protocol_events = { 317 .queue_sz = SCMI_PROTO_QUEUE_SZ, 318 .ops = &power_event_ops, 319 .evts = power_events, 320 .num_events = ARRAY_SIZE(power_events), 321 }; 322 323 static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) 324 { 325 int domain, ret; 326 u32 version; 327 struct scmi_power_info *pinfo; 328 329 ret = ph->xops->version_get(ph, &version); 330 if (ret) 331 return ret; 332 333 dev_dbg(ph->dev, "Power Version %d.%d\n", 334 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 335 336 pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 337 if (!pinfo) 338 return -ENOMEM; 339 340 ret = scmi_power_attributes_get(ph, pinfo); 341 if (ret) 342 return ret; 343 344 pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, 345 sizeof(*pinfo->dom_info), GFP_KERNEL); 346 if (!pinfo->dom_info) 347 return -ENOMEM; 348 349 for (domain = 0; domain < pinfo->num_domains; domain++) { 350 struct power_dom_info *dom = pinfo->dom_info + domain; 351 352 scmi_power_domain_attributes_get(ph, domain, dom, version, 353 pinfo->notify_state_change_cmd); 354 } 355 356 pinfo->version = version; 357 358 return ph->set_priv(ph, pinfo, version); 359 } 360 361 static const struct scmi_protocol scmi_power = { 362 .id = SCMI_PROTOCOL_POWER, 363 .owner = THIS_MODULE, 364 .instance_init = &scmi_power_protocol_init, 365 .ops = &power_proto_ops, 366 .events = &power_protocol_events, 367 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, 368 }; 369 370 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power) 371