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