383 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) Reset Protocol4 *5 * Copyright (C) 2019-2022 ARM Ltd.6 */7 8#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt9 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 0x3000118 19enum scmi_reset_protocol_cmd {20 RESET_DOMAIN_ATTRIBUTES = 0x3,21 RESET = 0x4,22 RESET_NOTIFY = 0x5,23 RESET_DOMAIN_NAME_GET = 0x6,24};25 26#define NUM_RESET_DOMAIN_MASK 0xffff27#define RESET_NOTIFY_ENABLE BIT(0)28 29struct scmi_msg_resp_reset_domain_attributes {30 __le32 attributes;31#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))32#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))33#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))34 __le32 latency;35 u8 name[SCMI_SHORT_NAME_MAX_SIZE];36};37 38struct scmi_msg_reset_domain_reset {39 __le32 domain_id;40 __le32 flags;41#define AUTONOMOUS_RESET BIT(0)42#define EXPLICIT_RESET_ASSERT BIT(1)43#define ASYNCHRONOUS_RESET BIT(2)44 __le32 reset_state;45#define ARCH_COLD_RESET 046};47 48struct scmi_msg_reset_notify {49 __le32 id;50 __le32 event_control;51#define RESET_TP_NOTIFY_ALL BIT(0)52};53 54struct scmi_reset_issued_notify_payld {55 __le32 agent_id;56 __le32 domain_id;57 __le32 reset_state;58};59 60struct reset_dom_info {61 bool async_reset;62 bool reset_notify;63 u32 latency_us;64 char name[SCMI_MAX_STR_SIZE];65};66 67struct scmi_reset_info {68 u32 version;69 int num_domains;70 bool notify_reset_cmd;71 struct reset_dom_info *dom_info;72};73 74static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,75 struct scmi_reset_info *pi)76{77 int ret;78 struct scmi_xfer *t;79 u32 attr;80 81 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,82 0, sizeof(attr), &t);83 if (ret)84 return ret;85 86 ret = ph->xops->do_xfer(ph, t);87 if (!ret) {88 attr = get_unaligned_le32(t->rx.buf);89 pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;90 }91 92 ph->xops->xfer_put(ph, t);93 94 if (!ret)95 if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))96 pi->notify_reset_cmd = true;97 98 return ret;99}100 101static int102scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,103 struct scmi_reset_info *pinfo,104 u32 domain, u32 version)105{106 int ret;107 u32 attributes;108 struct scmi_xfer *t;109 struct scmi_msg_resp_reset_domain_attributes *attr;110 struct reset_dom_info *dom_info = pinfo->dom_info + domain;111 112 ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,113 sizeof(domain), sizeof(*attr), &t);114 if (ret)115 return ret;116 117 put_unaligned_le32(domain, t->tx.buf);118 attr = t->rx.buf;119 120 ret = ph->xops->do_xfer(ph, t);121 if (!ret) {122 attributes = le32_to_cpu(attr->attributes);123 124 dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);125 if (pinfo->notify_reset_cmd)126 dom_info->reset_notify =127 SUPPORTS_NOTIFY_RESET(attributes);128 dom_info->latency_us = le32_to_cpu(attr->latency);129 if (dom_info->latency_us == U32_MAX)130 dom_info->latency_us = 0;131 strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);132 }133 134 ph->xops->xfer_put(ph, t);135 136 /*137 * If supported overwrite short name with the extended one;138 * on error just carry on and use already provided short name.139 */140 if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&141 SUPPORTS_EXTENDED_NAMES(attributes))142 ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,143 NULL, dom_info->name,144 SCMI_MAX_STR_SIZE);145 146 return ret;147}148 149static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)150{151 struct scmi_reset_info *pi = ph->get_priv(ph);152 153 return pi->num_domains;154}155 156static const char *157scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)158{159 struct scmi_reset_info *pi = ph->get_priv(ph);160 161 struct reset_dom_info *dom = pi->dom_info + domain;162 163 return dom->name;164}165 166static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,167 u32 domain)168{169 struct scmi_reset_info *pi = ph->get_priv(ph);170 struct reset_dom_info *dom = pi->dom_info + domain;171 172 return dom->latency_us;173}174 175static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,176 u32 flags, u32 state)177{178 int ret;179 struct scmi_xfer *t;180 struct scmi_msg_reset_domain_reset *dom;181 struct scmi_reset_info *pi = ph->get_priv(ph);182 struct reset_dom_info *rdom;183 184 if (domain >= pi->num_domains)185 return -EINVAL;186 187 rdom = pi->dom_info + domain;188 if (rdom->async_reset && flags & AUTONOMOUS_RESET)189 flags |= ASYNCHRONOUS_RESET;190 191 ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);192 if (ret)193 return ret;194 195 dom = t->tx.buf;196 dom->domain_id = cpu_to_le32(domain);197 dom->flags = cpu_to_le32(flags);198 dom->reset_state = cpu_to_le32(state);199 200 if (flags & ASYNCHRONOUS_RESET)201 ret = ph->xops->do_xfer_with_response(ph, t);202 else203 ret = ph->xops->do_xfer(ph, t);204 205 ph->xops->xfer_put(ph, t);206 return ret;207}208 209static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph,210 u32 domain)211{212 return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET,213 ARCH_COLD_RESET);214}215 216static int217scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain)218{219 return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT,220 ARCH_COLD_RESET);221}222 223static int224scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain)225{226 return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET);227}228 229static const struct scmi_reset_proto_ops reset_proto_ops = {230 .num_domains_get = scmi_reset_num_domains_get,231 .name_get = scmi_reset_name_get,232 .latency_get = scmi_reset_latency_get,233 .reset = scmi_reset_domain_reset,234 .assert = scmi_reset_domain_assert,235 .deassert = scmi_reset_domain_deassert,236};237 238static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,239 u8 evt_id, u32 src_id)240{241 struct reset_dom_info *dom;242 struct scmi_reset_info *pi = ph->get_priv(ph);243 244 if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)245 return false;246 247 dom = pi->dom_info + src_id;248 249 return dom->reset_notify;250}251 252static int scmi_reset_notify(const struct scmi_protocol_handle *ph,253 u32 domain_id, bool enable)254{255 int ret;256 u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;257 struct scmi_xfer *t;258 struct scmi_msg_reset_notify *cfg;259 260 ret = ph->xops->xfer_get_init(ph, RESET_NOTIFY, sizeof(*cfg), 0, &t);261 if (ret)262 return ret;263 264 cfg = t->tx.buf;265 cfg->id = cpu_to_le32(domain_id);266 cfg->event_control = cpu_to_le32(evt_cntl);267 268 ret = ph->xops->do_xfer(ph, t);269 270 ph->xops->xfer_put(ph, t);271 return ret;272}273 274static int scmi_reset_set_notify_enabled(const struct scmi_protocol_handle *ph,275 u8 evt_id, u32 src_id, bool enable)276{277 int ret;278 279 ret = scmi_reset_notify(ph, src_id, enable);280 if (ret)281 pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",282 evt_id, src_id, ret);283 284 return ret;285}286 287static void *288scmi_reset_fill_custom_report(const struct scmi_protocol_handle *ph,289 u8 evt_id, ktime_t timestamp,290 const void *payld, size_t payld_sz,291 void *report, u32 *src_id)292{293 const struct scmi_reset_issued_notify_payld *p = payld;294 struct scmi_reset_issued_report *r = report;295 296 if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz)297 return NULL;298 299 r->timestamp = timestamp;300 r->agent_id = le32_to_cpu(p->agent_id);301 r->domain_id = le32_to_cpu(p->domain_id);302 r->reset_state = le32_to_cpu(p->reset_state);303 *src_id = r->domain_id;304 305 return r;306}307 308static int scmi_reset_get_num_sources(const struct scmi_protocol_handle *ph)309{310 struct scmi_reset_info *pinfo = ph->get_priv(ph);311 312 if (!pinfo)313 return -EINVAL;314 315 return pinfo->num_domains;316}317 318static const struct scmi_event reset_events[] = {319 {320 .id = SCMI_EVENT_RESET_ISSUED,321 .max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld),322 .max_report_sz = sizeof(struct scmi_reset_issued_report),323 },324};325 326static const struct scmi_event_ops reset_event_ops = {327 .is_notify_supported = scmi_reset_notify_supported,328 .get_num_sources = scmi_reset_get_num_sources,329 .set_notify_enabled = scmi_reset_set_notify_enabled,330 .fill_custom_report = scmi_reset_fill_custom_report,331};332 333static const struct scmi_protocol_events reset_protocol_events = {334 .queue_sz = SCMI_PROTO_QUEUE_SZ,335 .ops = &reset_event_ops,336 .evts = reset_events,337 .num_events = ARRAY_SIZE(reset_events),338};339 340static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)341{342 int domain, ret;343 u32 version;344 struct scmi_reset_info *pinfo;345 346 ret = ph->xops->version_get(ph, &version);347 if (ret)348 return ret;349 350 dev_dbg(ph->dev, "Reset Version %d.%d\n",351 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));352 353 pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);354 if (!pinfo)355 return -ENOMEM;356 357 ret = scmi_reset_attributes_get(ph, pinfo);358 if (ret)359 return ret;360 361 pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,362 sizeof(*pinfo->dom_info), GFP_KERNEL);363 if (!pinfo->dom_info)364 return -ENOMEM;365 366 for (domain = 0; domain < pinfo->num_domains; domain++)367 scmi_reset_domain_attributes_get(ph, pinfo, domain, version);368 369 pinfo->version = version;370 return ph->set_priv(ph, pinfo, version);371}372 373static const struct scmi_protocol scmi_reset = {374 .id = SCMI_PROTOCOL_RESET,375 .owner = THIS_MODULE,376 .instance_init = &scmi_reset_protocol_init,377 .ops = &reset_proto_ops,378 .events = &reset_protocol_events,379 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,380};381 382DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)383