| .. | .. |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * System Control and Management Interface (SCMI) Power Protocol |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Copyright (C) 2018 ARM Ltd. |
|---|
| 5 | + * Copyright (C) 2018-2020 ARM Ltd. |
|---|
| 6 | 6 | */ |
|---|
| 7 | 7 | |
|---|
| 8 | +#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt |
|---|
| 9 | + |
|---|
| 10 | +#include <linux/module.h> |
|---|
| 11 | +#include <linux/scmi_protocol.h> |
|---|
| 12 | + |
|---|
| 8 | 13 | #include "common.h" |
|---|
| 14 | +#include "notify.h" |
|---|
| 9 | 15 | |
|---|
| 10 | 16 | enum scmi_power_protocol_cmd { |
|---|
| 11 | 17 | POWER_DOMAIN_ATTRIBUTES = 0x3, |
|---|
| .. | .. |
|---|
| 42 | 48 | __le32 notify_enable; |
|---|
| 43 | 49 | }; |
|---|
| 44 | 50 | |
|---|
| 51 | +struct scmi_power_state_notify_payld { |
|---|
| 52 | + __le32 agent_id; |
|---|
| 53 | + __le32 domain_id; |
|---|
| 54 | + __le32 power_state; |
|---|
| 55 | +}; |
|---|
| 56 | + |
|---|
| 45 | 57 | struct power_dom_info { |
|---|
| 46 | 58 | bool state_set_sync; |
|---|
| 47 | 59 | bool state_set_async; |
|---|
| .. | .. |
|---|
| 57 | 69 | struct power_dom_info *dom_info; |
|---|
| 58 | 70 | }; |
|---|
| 59 | 71 | |
|---|
| 60 | | -static int scmi_power_attributes_get(const struct scmi_handle *handle, |
|---|
| 72 | +static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, |
|---|
| 61 | 73 | struct scmi_power_info *pi) |
|---|
| 62 | 74 | { |
|---|
| 63 | 75 | int ret; |
|---|
| 64 | 76 | struct scmi_xfer *t; |
|---|
| 65 | 77 | struct scmi_msg_resp_power_attributes *attr; |
|---|
| 66 | 78 | |
|---|
| 67 | | - ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, |
|---|
| 68 | | - SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t); |
|---|
| 79 | + ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, |
|---|
| 80 | + 0, sizeof(*attr), &t); |
|---|
| 69 | 81 | if (ret) |
|---|
| 70 | 82 | return ret; |
|---|
| 71 | 83 | |
|---|
| 72 | 84 | attr = t->rx.buf; |
|---|
| 73 | 85 | |
|---|
| 74 | | - ret = scmi_do_xfer(handle, t); |
|---|
| 86 | + ret = ph->xops->do_xfer(ph, t); |
|---|
| 75 | 87 | if (!ret) { |
|---|
| 76 | 88 | pi->num_domains = le16_to_cpu(attr->num_domains); |
|---|
| 77 | 89 | pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | |
|---|
| .. | .. |
|---|
| 79 | 91 | pi->stats_size = le32_to_cpu(attr->stats_size); |
|---|
| 80 | 92 | } |
|---|
| 81 | 93 | |
|---|
| 82 | | - scmi_xfer_put(handle, t); |
|---|
| 94 | + ph->xops->xfer_put(ph, t); |
|---|
| 83 | 95 | return ret; |
|---|
| 84 | 96 | } |
|---|
| 85 | 97 | |
|---|
| 86 | 98 | static int |
|---|
| 87 | | -scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, |
|---|
| 88 | | - struct power_dom_info *dom_info) |
|---|
| 99 | +scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, |
|---|
| 100 | + u32 domain, struct power_dom_info *dom_info) |
|---|
| 89 | 101 | { |
|---|
| 90 | 102 | int ret; |
|---|
| 91 | 103 | struct scmi_xfer *t; |
|---|
| 92 | 104 | struct scmi_msg_resp_power_domain_attributes *attr; |
|---|
| 93 | 105 | |
|---|
| 94 | | - ret = scmi_xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES, |
|---|
| 95 | | - SCMI_PROTOCOL_POWER, sizeof(domain), |
|---|
| 96 | | - sizeof(*attr), &t); |
|---|
| 106 | + ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES, |
|---|
| 107 | + sizeof(domain), sizeof(*attr), &t); |
|---|
| 97 | 108 | if (ret) |
|---|
| 98 | 109 | return ret; |
|---|
| 99 | 110 | |
|---|
| 100 | 111 | put_unaligned_le32(domain, t->tx.buf); |
|---|
| 101 | 112 | attr = t->rx.buf; |
|---|
| 102 | 113 | |
|---|
| 103 | | - ret = scmi_do_xfer(handle, t); |
|---|
| 114 | + ret = ph->xops->do_xfer(ph, t); |
|---|
| 104 | 115 | if (!ret) { |
|---|
| 105 | 116 | u32 flags = le32_to_cpu(attr->flags); |
|---|
| 106 | 117 | |
|---|
| .. | .. |
|---|
| 110 | 121 | strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); |
|---|
| 111 | 122 | } |
|---|
| 112 | 123 | |
|---|
| 113 | | - scmi_xfer_put(handle, t); |
|---|
| 124 | + ph->xops->xfer_put(ph, t); |
|---|
| 114 | 125 | return ret; |
|---|
| 115 | 126 | } |
|---|
| 116 | 127 | |
|---|
| 117 | | -static int |
|---|
| 118 | | -scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state) |
|---|
| 128 | +static int scmi_power_state_set(const struct scmi_protocol_handle *ph, |
|---|
| 129 | + u32 domain, u32 state) |
|---|
| 119 | 130 | { |
|---|
| 120 | 131 | int ret; |
|---|
| 121 | 132 | struct scmi_xfer *t; |
|---|
| 122 | 133 | struct scmi_power_set_state *st; |
|---|
| 123 | 134 | |
|---|
| 124 | | - ret = scmi_xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER, |
|---|
| 125 | | - sizeof(*st), 0, &t); |
|---|
| 135 | + ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t); |
|---|
| 126 | 136 | if (ret) |
|---|
| 127 | 137 | return ret; |
|---|
| 128 | 138 | |
|---|
| .. | .. |
|---|
| 131 | 141 | st->domain = cpu_to_le32(domain); |
|---|
| 132 | 142 | st->state = cpu_to_le32(state); |
|---|
| 133 | 143 | |
|---|
| 134 | | - ret = scmi_do_xfer(handle, t); |
|---|
| 144 | + ret = ph->xops->do_xfer(ph, t); |
|---|
| 135 | 145 | |
|---|
| 136 | | - scmi_xfer_put(handle, t); |
|---|
| 146 | + ph->xops->xfer_put(ph, t); |
|---|
| 137 | 147 | return ret; |
|---|
| 138 | 148 | } |
|---|
| 139 | 149 | |
|---|
| 140 | | -static int |
|---|
| 141 | | -scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state) |
|---|
| 150 | +static int scmi_power_state_get(const struct scmi_protocol_handle *ph, |
|---|
| 151 | + u32 domain, u32 *state) |
|---|
| 142 | 152 | { |
|---|
| 143 | 153 | int ret; |
|---|
| 144 | 154 | struct scmi_xfer *t; |
|---|
| 145 | 155 | |
|---|
| 146 | | - ret = scmi_xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER, |
|---|
| 147 | | - sizeof(u32), sizeof(u32), &t); |
|---|
| 156 | + ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t); |
|---|
| 148 | 157 | if (ret) |
|---|
| 149 | 158 | return ret; |
|---|
| 150 | 159 | |
|---|
| 151 | 160 | put_unaligned_le32(domain, t->tx.buf); |
|---|
| 152 | 161 | |
|---|
| 153 | | - ret = scmi_do_xfer(handle, t); |
|---|
| 162 | + ret = ph->xops->do_xfer(ph, t); |
|---|
| 154 | 163 | if (!ret) |
|---|
| 155 | 164 | *state = get_unaligned_le32(t->rx.buf); |
|---|
| 156 | 165 | |
|---|
| 157 | | - scmi_xfer_put(handle, t); |
|---|
| 166 | + ph->xops->xfer_put(ph, t); |
|---|
| 158 | 167 | return ret; |
|---|
| 159 | 168 | } |
|---|
| 160 | 169 | |
|---|
| 161 | | -static int scmi_power_num_domains_get(const struct scmi_handle *handle) |
|---|
| 170 | +static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph) |
|---|
| 162 | 171 | { |
|---|
| 163 | | - struct scmi_power_info *pi = handle->power_priv; |
|---|
| 172 | + struct scmi_power_info *pi = ph->get_priv(ph); |
|---|
| 164 | 173 | |
|---|
| 165 | 174 | return pi->num_domains; |
|---|
| 166 | 175 | } |
|---|
| 167 | 176 | |
|---|
| 168 | | -static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain) |
|---|
| 177 | +static char *scmi_power_name_get(const struct scmi_protocol_handle *ph, |
|---|
| 178 | + u32 domain) |
|---|
| 169 | 179 | { |
|---|
| 170 | | - struct scmi_power_info *pi = handle->power_priv; |
|---|
| 180 | + struct scmi_power_info *pi = ph->get_priv(ph); |
|---|
| 171 | 181 | struct power_dom_info *dom = pi->dom_info + domain; |
|---|
| 172 | 182 | |
|---|
| 173 | 183 | return dom->name; |
|---|
| 174 | 184 | } |
|---|
| 175 | 185 | |
|---|
| 176 | | -static struct scmi_power_ops power_ops = { |
|---|
| 186 | +static const struct scmi_power_proto_ops power_proto_ops = { |
|---|
| 177 | 187 | .num_domains_get = scmi_power_num_domains_get, |
|---|
| 178 | 188 | .name_get = scmi_power_name_get, |
|---|
| 179 | 189 | .state_set = scmi_power_state_set, |
|---|
| 180 | 190 | .state_get = scmi_power_state_get, |
|---|
| 181 | 191 | }; |
|---|
| 182 | 192 | |
|---|
| 183 | | -static int scmi_power_protocol_init(struct scmi_handle *handle) |
|---|
| 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) |
|---|
| 184 | 282 | { |
|---|
| 185 | 283 | int domain; |
|---|
| 186 | 284 | u32 version; |
|---|
| 187 | 285 | struct scmi_power_info *pinfo; |
|---|
| 188 | 286 | |
|---|
| 189 | | - scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version); |
|---|
| 287 | + ph->xops->version_get(ph, &version); |
|---|
| 190 | 288 | |
|---|
| 191 | | - dev_dbg(handle->dev, "Power Version %d.%d\n", |
|---|
| 289 | + dev_dbg(ph->dev, "Power Version %d.%d\n", |
|---|
| 192 | 290 | PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); |
|---|
| 193 | 291 | |
|---|
| 194 | | - pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); |
|---|
| 292 | + pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); |
|---|
| 195 | 293 | if (!pinfo) |
|---|
| 196 | 294 | return -ENOMEM; |
|---|
| 197 | 295 | |
|---|
| 198 | | - scmi_power_attributes_get(handle, pinfo); |
|---|
| 296 | + scmi_power_attributes_get(ph, pinfo); |
|---|
| 199 | 297 | |
|---|
| 200 | | - pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains, |
|---|
| 298 | + pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, |
|---|
| 201 | 299 | sizeof(*pinfo->dom_info), GFP_KERNEL); |
|---|
| 202 | 300 | if (!pinfo->dom_info) |
|---|
| 203 | 301 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 205 | 303 | for (domain = 0; domain < pinfo->num_domains; domain++) { |
|---|
| 206 | 304 | struct power_dom_info *dom = pinfo->dom_info + domain; |
|---|
| 207 | 305 | |
|---|
| 208 | | - scmi_power_domain_attributes_get(handle, domain, dom); |
|---|
| 306 | + scmi_power_domain_attributes_get(ph, domain, dom); |
|---|
| 209 | 307 | } |
|---|
| 210 | 308 | |
|---|
| 211 | 309 | pinfo->version = version; |
|---|
| 212 | | - handle->power_ops = &power_ops; |
|---|
| 213 | | - handle->power_priv = pinfo; |
|---|
| 214 | 310 | |
|---|
| 215 | | - return 0; |
|---|
| 311 | + return ph->set_priv(ph, pinfo); |
|---|
| 216 | 312 | } |
|---|
| 217 | 313 | |
|---|
| 218 | | -DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_POWER, power) |
|---|
| 314 | +static const struct scmi_protocol scmi_power = { |
|---|
| 315 | + .id = SCMI_PROTOCOL_POWER, |
|---|
| 316 | + .owner = THIS_MODULE, |
|---|
| 317 | + .init_instance = &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) |
|---|