1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) Power Protocol 4 * 5 * Copyright (C) 2018 ARM Ltd. 6 */ 7 8 #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt 9 10 #include <linux/scmi_protocol.h> 11 12 #include "common.h" 13 #include "notify.h" 14 15 enum scmi_power_protocol_cmd { 16 POWER_DOMAIN_ATTRIBUTES = 0x3, 17 POWER_STATE_SET = 0x4, 18 POWER_STATE_GET = 0x5, 19 POWER_STATE_NOTIFY = 0x6, 20 }; 21 22 struct scmi_msg_resp_power_attributes { 23 __le16 num_domains; 24 __le16 reserved; 25 __le32 stats_addr_low; 26 __le32 stats_addr_high; 27 __le32 stats_size; 28 }; 29 30 struct scmi_msg_resp_power_domain_attributes { 31 __le32 flags; 32 #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31)) 33 #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30)) 34 #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29)) 35 u8 name[SCMI_MAX_STR_SIZE]; 36 }; 37 38 struct scmi_power_set_state { 39 __le32 flags; 40 #define STATE_SET_ASYNC BIT(0) 41 __le32 domain; 42 __le32 state; 43 }; 44 45 struct scmi_power_state_notify { 46 __le32 domain; 47 __le32 notify_enable; 48 }; 49 50 struct scmi_power_state_notify_payld { 51 __le32 agent_id; 52 __le32 domain_id; 53 __le32 power_state; 54 }; 55 56 struct power_dom_info { 57 bool state_set_sync; 58 bool state_set_async; 59 bool state_set_notify; 60 char name[SCMI_MAX_STR_SIZE]; 61 }; 62 63 struct scmi_power_info { 64 u32 version; 65 int num_domains; 66 u64 stats_addr; 67 u32 stats_size; 68 struct power_dom_info *dom_info; 69 }; 70 71 static int scmi_power_attributes_get(const struct scmi_handle *handle, 72 struct scmi_power_info *pi) 73 { 74 int ret; 75 struct scmi_xfer *t; 76 struct scmi_msg_resp_power_attributes *attr; 77 78 ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, 79 SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t); 80 if (ret) 81 return ret; 82 83 attr = t->rx.buf; 84 85 ret = scmi_do_xfer(handle, t); 86 if (!ret) { 87 pi->num_domains = le16_to_cpu(attr->num_domains); 88 pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | 89 (u64)le32_to_cpu(attr->stats_addr_high) << 32; 90 pi->stats_size = le32_to_cpu(attr->stats_size); 91 } 92 93 scmi_xfer_put(handle, t); 94 return ret; 95 } 96 97 static int 98 scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, 99 struct power_dom_info *dom_info) 100 { 101 int ret; 102 struct scmi_xfer *t; 103 struct scmi_msg_resp_power_domain_attributes *attr; 104 105 ret = scmi_xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES, 106 SCMI_PROTOCOL_POWER, sizeof(domain), 107 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 = scmi_do_xfer(handle, 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 scmi_xfer_put(handle, t); 125 return ret; 126 } 127 128 static int 129 scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state) 130 { 131 int ret; 132 struct scmi_xfer *t; 133 struct scmi_power_set_state *st; 134 135 ret = scmi_xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER, 136 sizeof(*st), 0, &t); 137 if (ret) 138 return ret; 139 140 st = t->tx.buf; 141 st->flags = cpu_to_le32(0); 142 st->domain = cpu_to_le32(domain); 143 st->state = cpu_to_le32(state); 144 145 ret = scmi_do_xfer(handle, t); 146 147 scmi_xfer_put(handle, t); 148 return ret; 149 } 150 151 static int 152 scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state) 153 { 154 int ret; 155 struct scmi_xfer *t; 156 157 ret = scmi_xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER, 158 sizeof(u32), sizeof(u32), &t); 159 if (ret) 160 return ret; 161 162 put_unaligned_le32(domain, t->tx.buf); 163 164 ret = scmi_do_xfer(handle, t); 165 if (!ret) 166 *state = get_unaligned_le32(t->rx.buf); 167 168 scmi_xfer_put(handle, t); 169 return ret; 170 } 171 172 static int scmi_power_num_domains_get(const struct scmi_handle *handle) 173 { 174 struct scmi_power_info *pi = handle->power_priv; 175 176 return pi->num_domains; 177 } 178 179 static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain) 180 { 181 struct scmi_power_info *pi = handle->power_priv; 182 struct power_dom_info *dom = pi->dom_info + domain; 183 184 return dom->name; 185 } 186 187 static const struct scmi_power_ops power_ops = { 188 .num_domains_get = scmi_power_num_domains_get, 189 .name_get = scmi_power_name_get, 190 .state_set = scmi_power_state_set, 191 .state_get = scmi_power_state_get, 192 }; 193 194 static int scmi_power_request_notify(const struct scmi_handle *handle, 195 u32 domain, bool enable) 196 { 197 int ret; 198 struct scmi_xfer *t; 199 struct scmi_power_state_notify *notify; 200 201 ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY, 202 SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t); 203 if (ret) 204 return ret; 205 206 notify = t->tx.buf; 207 notify->domain = cpu_to_le32(domain); 208 notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; 209 210 ret = scmi_do_xfer(handle, t); 211 212 scmi_xfer_put(handle, t); 213 return ret; 214 } 215 216 static int scmi_power_set_notify_enabled(const struct scmi_handle *handle, 217 u8 evt_id, u32 src_id, bool enable) 218 { 219 int ret; 220 221 ret = scmi_power_request_notify(handle, src_id, enable); 222 if (ret) 223 pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n", 224 evt_id, src_id, ret); 225 226 return ret; 227 } 228 229 static void *scmi_power_fill_custom_report(const struct scmi_handle *handle, 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 const struct scmi_event power_events[] = { 250 { 251 .id = SCMI_EVENT_POWER_STATE_CHANGED, 252 .max_payld_sz = sizeof(struct scmi_power_state_notify_payld), 253 .max_report_sz = 254 sizeof(struct scmi_power_state_changed_report), 255 }, 256 }; 257 258 static const struct scmi_event_ops power_event_ops = { 259 .set_notify_enabled = scmi_power_set_notify_enabled, 260 .fill_custom_report = scmi_power_fill_custom_report, 261 }; 262 263 static int scmi_power_protocol_init(struct scmi_handle *handle) 264 { 265 int domain; 266 u32 version; 267 struct scmi_power_info *pinfo; 268 269 scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version); 270 271 dev_dbg(handle->dev, "Power Version %d.%d\n", 272 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 273 274 pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); 275 if (!pinfo) 276 return -ENOMEM; 277 278 scmi_power_attributes_get(handle, pinfo); 279 280 pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains, 281 sizeof(*pinfo->dom_info), GFP_KERNEL); 282 if (!pinfo->dom_info) 283 return -ENOMEM; 284 285 for (domain = 0; domain < pinfo->num_domains; domain++) { 286 struct power_dom_info *dom = pinfo->dom_info + domain; 287 288 scmi_power_domain_attributes_get(handle, domain, dom); 289 } 290 291 scmi_register_protocol_events(handle, 292 SCMI_PROTOCOL_POWER, SCMI_PROTO_QUEUE_SZ, 293 &power_event_ops, power_events, 294 ARRAY_SIZE(power_events), 295 pinfo->num_domains); 296 297 pinfo->version = version; 298 handle->power_ops = &power_ops; 299 handle->power_priv = pinfo; 300 301 return 0; 302 } 303 304 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_POWER, power) 305