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