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 = pi->dom_info + domain; 208 209 return dom->name; 210 } 211 212 static const struct scmi_power_proto_ops power_proto_ops = { 213 .num_domains_get = scmi_power_num_domains_get, 214 .name_get = scmi_power_name_get, 215 .state_set = scmi_power_state_set, 216 .state_get = scmi_power_state_get, 217 }; 218 219 static int scmi_power_request_notify(const struct scmi_protocol_handle *ph, 220 u32 domain, bool enable) 221 { 222 int ret; 223 struct scmi_xfer *t; 224 struct scmi_power_state_notify *notify; 225 226 ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY, 227 sizeof(*notify), 0, &t); 228 if (ret) 229 return ret; 230 231 notify = t->tx.buf; 232 notify->domain = cpu_to_le32(domain); 233 notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; 234 235 ret = ph->xops->do_xfer(ph, t); 236 237 ph->xops->xfer_put(ph, t); 238 return ret; 239 } 240 241 static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph, 242 u8 evt_id, u32 src_id) 243 { 244 struct power_dom_info *dom; 245 struct scmi_power_info *pinfo = ph->get_priv(ph); 246 247 if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || 248 src_id >= pinfo->num_domains) 249 return false; 250 251 dom = pinfo->dom_info + src_id; 252 return dom->state_set_notify; 253 } 254 255 static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph, 256 u8 evt_id, u32 src_id, bool enable) 257 { 258 int ret; 259 260 ret = scmi_power_request_notify(ph, src_id, enable); 261 if (ret) 262 pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n", 263 evt_id, src_id, ret); 264 265 return ret; 266 } 267 268 static void * 269 scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph, 270 u8 evt_id, ktime_t timestamp, 271 const void *payld, size_t payld_sz, 272 void *report, u32 *src_id) 273 { 274 const struct scmi_power_state_notify_payld *p = payld; 275 struct scmi_power_state_changed_report *r = report; 276 277 if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz) 278 return NULL; 279 280 r->timestamp = timestamp; 281 r->agent_id = le32_to_cpu(p->agent_id); 282 r->domain_id = le32_to_cpu(p->domain_id); 283 r->power_state = le32_to_cpu(p->power_state); 284 *src_id = r->domain_id; 285 286 return r; 287 } 288 289 static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph) 290 { 291 struct scmi_power_info *pinfo = ph->get_priv(ph); 292 293 if (!pinfo) 294 return -EINVAL; 295 296 return pinfo->num_domains; 297 } 298 299 static const struct scmi_event power_events[] = { 300 { 301 .id = SCMI_EVENT_POWER_STATE_CHANGED, 302 .max_payld_sz = sizeof(struct scmi_power_state_notify_payld), 303 .max_report_sz = 304 sizeof(struct scmi_power_state_changed_report), 305 }, 306 }; 307 308 static const struct scmi_event_ops power_event_ops = { 309 .is_notify_supported = scmi_power_notify_supported, 310 .get_num_sources = scmi_power_get_num_sources, 311 .set_notify_enabled = scmi_power_set_notify_enabled, 312 .fill_custom_report = scmi_power_fill_custom_report, 313 }; 314 315 static const struct scmi_protocol_events power_protocol_events = { 316 .queue_sz = SCMI_PROTO_QUEUE_SZ, 317 .ops = &power_event_ops, 318 .evts = power_events, 319 .num_events = ARRAY_SIZE(power_events), 320 }; 321 322 static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) 323 { 324 int domain, ret; 325 struct scmi_power_info *pinfo; 326 327 dev_dbg(ph->dev, "Power Version %d.%d\n", 328 PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version)); 329 330 pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 331 if (!pinfo) 332 return -ENOMEM; 333 334 ret = scmi_power_attributes_get(ph, pinfo); 335 if (ret) 336 return ret; 337 338 pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, 339 sizeof(*pinfo->dom_info), GFP_KERNEL); 340 if (!pinfo->dom_info) 341 return -ENOMEM; 342 343 for (domain = 0; domain < pinfo->num_domains; domain++) { 344 struct power_dom_info *dom = pinfo->dom_info + domain; 345 346 scmi_power_domain_attributes_get(ph, domain, dom, 347 pinfo->notify_state_change_cmd); 348 } 349 350 return ph->set_priv(ph, pinfo); 351 } 352 353 static const struct scmi_protocol scmi_power = { 354 .id = SCMI_PROTOCOL_POWER, 355 .owner = THIS_MODULE, 356 .instance_init = &scmi_power_protocol_init, 357 .ops = &power_proto_ops, 358 .events = &power_protocol_events, 359 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, 360 }; 361 362 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power) 363