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