Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 362 lines 8.9 kB view raw
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 19enum 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 27struct 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 35struct 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 44struct scmi_power_set_state { 45 __le32 flags; 46#define STATE_SET_ASYNC BIT(0) 47 __le32 domain; 48 __le32 state; 49}; 50 51struct scmi_power_state_notify { 52 __le32 domain; 53 __le32 notify_enable; 54}; 55 56struct scmi_power_state_notify_payld { 57 __le32 agent_id; 58 __le32 domain_id; 59 __le32 power_state; 60}; 61 62struct 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 69struct 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 77static 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 108static int 109scmi_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 153static 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 175static 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 195static 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 202static const char * 203scmi_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 212static 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 219static 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 241static 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 255static 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 268static void * 269scmi_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 289static 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 299static 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 308static 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 315static 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 322static 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 353static 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 362DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)