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