431 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) Base Protocol4 *5 * Copyright (C) 2018-2021 ARM Ltd.6 */7 8#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt9 10#include <linux/module.h>11#include <linux/scmi_protocol.h>12 13#include "common.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 0x2000118 19#define SCMI_BASE_NUM_SOURCES 120#define SCMI_BASE_MAX_CMD_ERR_COUNT 102421 22enum scmi_base_protocol_cmd {23 BASE_DISCOVER_VENDOR = 0x3,24 BASE_DISCOVER_SUB_VENDOR = 0x4,25 BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,26 BASE_DISCOVER_LIST_PROTOCOLS = 0x6,27 BASE_DISCOVER_AGENT = 0x7,28 BASE_NOTIFY_ERRORS = 0x8,29 BASE_SET_DEVICE_PERMISSIONS = 0x9,30 BASE_SET_PROTOCOL_PERMISSIONS = 0xa,31 BASE_RESET_AGENT_CONFIGURATION = 0xb,32};33 34struct scmi_msg_resp_base_attributes {35 u8 num_protocols;36 u8 num_agents;37 __le16 reserved;38};39 40struct scmi_msg_resp_base_discover_agent {41 __le32 agent_id;42 u8 name[SCMI_SHORT_NAME_MAX_SIZE];43};44 45struct scmi_msg_base_error_notify {46 __le32 event_control;47#define BASE_TP_NOTIFY_ALL BIT(0)48};49 50struct scmi_base_error_notify_payld {51 __le32 agent_id;52 __le32 error_status;53#define IS_FATAL_ERROR(x) ((x) & BIT(31))54#define ERROR_CMD_COUNT(x) FIELD_GET(GENMASK(9, 0), (x))55 __le64 msg_reports[SCMI_BASE_MAX_CMD_ERR_COUNT];56};57 58/**59 * scmi_base_attributes_get() - gets the implementation details60 * that are associated with the base protocol.61 *62 * @ph: SCMI protocol handle63 *64 * Return: 0 on success, else appropriate SCMI error.65 */66static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph)67{68 int ret;69 struct scmi_xfer *t;70 struct scmi_msg_resp_base_attributes *attr_info;71 struct scmi_revision_info *rev = ph->get_priv(ph);72 73 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,74 0, sizeof(*attr_info), &t);75 if (ret)76 return ret;77 78 ret = ph->xops->do_xfer(ph, t);79 if (!ret) {80 attr_info = t->rx.buf;81 rev->num_protocols = attr_info->num_protocols;82 rev->num_agents = attr_info->num_agents;83 }84 85 ph->xops->xfer_put(ph, t);86 87 return ret;88}89 90/**91 * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.92 *93 * @ph: SCMI protocol handle94 * @sub_vendor: specify true if sub-vendor ID is needed95 *96 * Return: 0 on success, else appropriate SCMI error.97 */98static int99scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)100{101 u8 cmd;102 int ret, size;103 char *vendor_id;104 struct scmi_xfer *t;105 struct scmi_revision_info *rev = ph->get_priv(ph);106 107 if (sub_vendor) {108 cmd = BASE_DISCOVER_SUB_VENDOR;109 vendor_id = rev->sub_vendor_id;110 size = ARRAY_SIZE(rev->sub_vendor_id);111 } else {112 cmd = BASE_DISCOVER_VENDOR;113 vendor_id = rev->vendor_id;114 size = ARRAY_SIZE(rev->vendor_id);115 }116 117 ret = ph->xops->xfer_get_init(ph, cmd, 0, size, &t);118 if (ret)119 return ret;120 121 ret = ph->xops->do_xfer(ph, t);122 if (!ret)123 strscpy(vendor_id, t->rx.buf, size);124 125 ph->xops->xfer_put(ph, t);126 127 return ret;128}129 130/**131 * scmi_base_implementation_version_get() - gets a vendor-specific132 * implementation 32-bit version. The format of the version number is133 * vendor-specific134 *135 * @ph: SCMI protocol handle136 *137 * Return: 0 on success, else appropriate SCMI error.138 */139static int140scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph)141{142 int ret;143 __le32 *impl_ver;144 struct scmi_xfer *t;145 struct scmi_revision_info *rev = ph->get_priv(ph);146 147 ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION,148 0, sizeof(*impl_ver), &t);149 if (ret)150 return ret;151 152 ret = ph->xops->do_xfer(ph, t);153 if (!ret) {154 impl_ver = t->rx.buf;155 rev->impl_ver = le32_to_cpu(*impl_ver);156 }157 158 ph->xops->xfer_put(ph, t);159 160 return ret;161}162 163/**164 * scmi_base_implementation_list_get() - gets the list of protocols it is165 * OSPM is allowed to access166 *167 * @ph: SCMI protocol handle168 * @protocols_imp: pointer to hold the list of protocol identifiers169 *170 * Return: 0 on success, else appropriate SCMI error.171 */172static int173scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,174 u8 *protocols_imp)175{176 u8 *list;177 int ret, loop;178 struct scmi_xfer *t;179 __le32 *num_skip, *num_ret;180 u32 tot_num_ret = 0, loop_num_ret;181 struct device *dev = ph->dev;182 struct scmi_revision_info *rev = ph->get_priv(ph);183 184 ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,185 sizeof(*num_skip), 0, &t);186 if (ret)187 return ret;188 189 num_skip = t->tx.buf;190 num_ret = t->rx.buf;191 list = t->rx.buf + sizeof(*num_ret);192 193 do {194 size_t real_list_sz;195 u32 calc_list_sz;196 197 /* Set the number of protocols to be skipped/already read */198 *num_skip = cpu_to_le32(tot_num_ret);199 200 ret = ph->xops->do_xfer(ph, t);201 if (ret)202 break;203 204 loop_num_ret = le32_to_cpu(*num_ret);205 if (!loop_num_ret)206 break;207 208 if (loop_num_ret > rev->num_protocols - tot_num_ret) {209 dev_err(dev,210 "No. Returned protocols > Total protocols.\n");211 break;212 }213 214 if (t->rx.len < (sizeof(u32) * 2)) {215 dev_err(dev, "Truncated reply - rx.len:%zd\n",216 t->rx.len);217 ret = -EPROTO;218 break;219 }220 221 real_list_sz = t->rx.len - sizeof(u32);222 calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) *223 sizeof(u32);224 if (calc_list_sz != real_list_sz) {225 dev_warn(dev,226 "Malformed reply - real_sz:%zd calc_sz:%u (loop_num_ret:%d)\n",227 real_list_sz, calc_list_sz, loop_num_ret);228 /*229 * Bail out if the expected list size is bigger than the230 * total payload size of the received reply.231 */232 if (calc_list_sz > real_list_sz) {233 ret = -EPROTO;234 break;235 }236 }237 238 for (loop = 0; loop < loop_num_ret; loop++)239 protocols_imp[tot_num_ret + loop] = *(list + loop);240 241 tot_num_ret += loop_num_ret;242 243 ph->xops->reset_rx_to_maxsz(ph, t);244 } while (tot_num_ret < rev->num_protocols);245 246 ph->xops->xfer_put(ph, t);247 248 return ret;249}250 251/**252 * scmi_base_discover_agent_get() - discover the name of an agent253 *254 * @ph: SCMI protocol handle255 * @id: Agent identifier256 * @name: Agent identifier ASCII string257 *258 * An agent id of 0 is reserved to identify the platform itself.259 * Generally operating system is represented as "OSPM"260 *261 * Return: 0 on success, else appropriate SCMI error.262 */263static int scmi_base_discover_agent_get(const struct scmi_protocol_handle *ph,264 int id, char *name)265{266 int ret;267 struct scmi_msg_resp_base_discover_agent *agent_info;268 struct scmi_xfer *t;269 270 ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_AGENT,271 sizeof(__le32), sizeof(*agent_info), &t);272 if (ret)273 return ret;274 275 put_unaligned_le32(id, t->tx.buf);276 277 ret = ph->xops->do_xfer(ph, t);278 if (!ret) {279 agent_info = t->rx.buf;280 strscpy(name, agent_info->name, SCMI_SHORT_NAME_MAX_SIZE);281 }282 283 ph->xops->xfer_put(ph, t);284 285 return ret;286}287 288static int scmi_base_error_notify(const struct scmi_protocol_handle *ph,289 bool enable)290{291 int ret;292 u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0;293 struct scmi_xfer *t;294 struct scmi_msg_base_error_notify *cfg;295 296 ret = ph->xops->xfer_get_init(ph, BASE_NOTIFY_ERRORS,297 sizeof(*cfg), 0, &t);298 if (ret)299 return ret;300 301 cfg = t->tx.buf;302 cfg->event_control = cpu_to_le32(evt_cntl);303 304 ret = ph->xops->do_xfer(ph, t);305 306 ph->xops->xfer_put(ph, t);307 return ret;308}309 310static int scmi_base_set_notify_enabled(const struct scmi_protocol_handle *ph,311 u8 evt_id, u32 src_id, bool enable)312{313 int ret;314 315 ret = scmi_base_error_notify(ph, enable);316 if (ret)317 pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret);318 319 return ret;320}321 322static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,323 u8 evt_id, ktime_t timestamp,324 const void *payld, size_t payld_sz,325 void *report, u32 *src_id)326{327 int i;328 const struct scmi_base_error_notify_payld *p = payld;329 struct scmi_base_error_report *r = report;330 331 /*332 * BaseError notification payload is variable in size but333 * up to a maximum length determined by the struct ponted by p.334 * Instead payld_sz is the effective length of this notification335 * payload so cannot be greater of the maximum allowed size as336 * pointed by p.337 */338 if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz)339 return NULL;340 341 r->timestamp = timestamp;342 r->agent_id = le32_to_cpu(p->agent_id);343 r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status));344 r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status));345 for (i = 0; i < r->cmd_count; i++)346 r->reports[i] = le64_to_cpu(p->msg_reports[i]);347 *src_id = 0;348 349 return r;350}351 352static const struct scmi_event base_events[] = {353 {354 .id = SCMI_EVENT_BASE_ERROR_EVENT,355 .max_payld_sz = sizeof(struct scmi_base_error_notify_payld),356 .max_report_sz = sizeof(struct scmi_base_error_report) +357 SCMI_BASE_MAX_CMD_ERR_COUNT * sizeof(u64),358 },359};360 361static const struct scmi_event_ops base_event_ops = {362 .set_notify_enabled = scmi_base_set_notify_enabled,363 .fill_custom_report = scmi_base_fill_custom_report,364};365 366static const struct scmi_protocol_events base_protocol_events = {367 .queue_sz = 4 * SCMI_PROTO_QUEUE_SZ,368 .ops = &base_event_ops,369 .evts = base_events,370 .num_events = ARRAY_SIZE(base_events),371 .num_sources = SCMI_BASE_NUM_SOURCES,372};373 374static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)375{376 int id, ret;377 u8 *prot_imp;378 u32 version;379 char name[SCMI_SHORT_NAME_MAX_SIZE];380 struct device *dev = ph->dev;381 struct scmi_revision_info *rev = scmi_revision_area_get(ph);382 383 ret = ph->xops->version_get(ph, &version);384 if (ret)385 return ret;386 387 rev->major_ver = PROTOCOL_REV_MAJOR(version);388 rev->minor_ver = PROTOCOL_REV_MINOR(version);389 ph->set_priv(ph, rev, version);390 391 ret = scmi_base_attributes_get(ph);392 if (ret)393 return ret;394 395 prot_imp = devm_kcalloc(dev, rev->num_protocols, sizeof(u8),396 GFP_KERNEL);397 if (!prot_imp)398 return -ENOMEM;399 400 scmi_base_vendor_id_get(ph, false);401 scmi_base_vendor_id_get(ph, true);402 scmi_base_implementation_version_get(ph);403 scmi_base_implementation_list_get(ph, prot_imp);404 405 scmi_setup_protocol_implemented(ph, prot_imp);406 407 dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",408 rev->major_ver, rev->minor_ver, rev->vendor_id,409 rev->sub_vendor_id, rev->impl_ver);410 dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,411 rev->num_agents);412 413 for (id = 0; id < rev->num_agents; id++) {414 scmi_base_discover_agent_get(ph, id, name);415 dev_dbg(dev, "Agent %d: %s\n", id, name);416 }417 418 return 0;419}420 421static const struct scmi_protocol scmi_base = {422 .id = SCMI_PROTOCOL_BASE,423 .owner = NULL,424 .instance_init = &scmi_base_protocol_init,425 .ops = NULL,426 .events = &base_protocol_events,427 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,428};429 430DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)431