linux/drivers/firmware/arm_scmi/voltage.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * System Control and Management Interface (SCMI) Voltage Protocol
   4 *
   5 * Copyright (C) 2020-2021 ARM Ltd.
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/scmi_protocol.h>
  10
  11#include "common.h"
  12
  13#define VOLTAGE_DOMS_NUM_MASK           GENMASK(15, 0)
  14#define REMAINING_LEVELS_MASK           GENMASK(31, 16)
  15#define RETURNED_LEVELS_MASK            GENMASK(11, 0)
  16
  17enum scmi_voltage_protocol_cmd {
  18        VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
  19        VOLTAGE_DESCRIBE_LEVELS = 0x4,
  20        VOLTAGE_CONFIG_SET = 0x5,
  21        VOLTAGE_CONFIG_GET = 0x6,
  22        VOLTAGE_LEVEL_SET = 0x7,
  23        VOLTAGE_LEVEL_GET = 0x8,
  24};
  25
  26#define NUM_VOLTAGE_DOMAINS(x)  ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
  27
  28struct scmi_msg_resp_domain_attributes {
  29        __le32 attr;
  30        u8 name[SCMI_MAX_STR_SIZE];
  31};
  32
  33struct scmi_msg_cmd_describe_levels {
  34        __le32 domain_id;
  35        __le32 level_index;
  36};
  37
  38struct scmi_msg_resp_describe_levels {
  39        __le32 flags;
  40#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
  41#define NUM_RETURNED_LEVELS(f)  ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
  42#define SUPPORTS_SEGMENTED_LEVELS(f)    ((f) & BIT(12))
  43        __le32 voltage[];
  44};
  45
  46struct scmi_msg_cmd_config_set {
  47        __le32 domain_id;
  48        __le32 config;
  49};
  50
  51struct scmi_msg_cmd_level_set {
  52        __le32 domain_id;
  53        __le32 flags;
  54        __le32 voltage_level;
  55};
  56
  57struct voltage_info {
  58        unsigned int version;
  59        unsigned int num_domains;
  60        struct scmi_voltage_info *domains;
  61};
  62
  63static int scmi_protocol_attributes_get(const struct scmi_protocol_handle *ph,
  64                                        struct voltage_info *vinfo)
  65{
  66        int ret;
  67        struct scmi_xfer *t;
  68
  69        ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
  70                                      sizeof(__le32), &t);
  71        if (ret)
  72                return ret;
  73
  74        ret = ph->xops->do_xfer(ph, t);
  75        if (!ret)
  76                vinfo->num_domains =
  77                        NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
  78
  79        ph->xops->xfer_put(ph, t);
  80        return ret;
  81}
  82
  83static int scmi_init_voltage_levels(struct device *dev,
  84                                    struct scmi_voltage_info *v,
  85                                    u32 num_returned, u32 num_remaining,
  86                                    bool segmented)
  87{
  88        u32 num_levels;
  89
  90        num_levels = num_returned + num_remaining;
  91        /*
  92         * segmented levels entries are represented by a single triplet
  93         * returned all in one go.
  94         */
  95        if (!num_levels ||
  96            (segmented && (num_remaining || num_returned != 3))) {
  97                dev_err(dev,
  98                        "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
  99                        num_levels, num_returned, num_remaining, v->id);
 100                return -EINVAL;
 101        }
 102
 103        v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
 104        if (!v->levels_uv)
 105                return -ENOMEM;
 106
 107        v->num_levels = num_levels;
 108        v->segmented = segmented;
 109
 110        return 0;
 111}
 112
 113static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
 114                                        struct voltage_info *vinfo)
 115{
 116        int ret, dom;
 117        struct scmi_xfer *td, *tl;
 118        struct device *dev = ph->dev;
 119        struct scmi_msg_resp_domain_attributes *resp_dom;
 120        struct scmi_msg_resp_describe_levels *resp_levels;
 121
 122        ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
 123                                      sizeof(__le32), sizeof(*resp_dom), &td);
 124        if (ret)
 125                return ret;
 126        resp_dom = td->rx.buf;
 127
 128        ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
 129                                      sizeof(__le64), 0, &tl);
 130        if (ret)
 131                goto outd;
 132        resp_levels = tl->rx.buf;
 133
 134        for (dom = 0; dom < vinfo->num_domains; dom++) {
 135                u32 desc_index = 0;
 136                u16 num_returned = 0, num_remaining = 0;
 137                struct scmi_msg_cmd_describe_levels *cmd;
 138                struct scmi_voltage_info *v;
 139
 140                /* Retrieve domain attributes at first ... */
 141                put_unaligned_le32(dom, td->tx.buf);
 142                ret = ph->xops->do_xfer(ph, td);
 143                /* Skip domain on comms error */
 144                if (ret)
 145                        continue;
 146
 147                v = vinfo->domains + dom;
 148                v->id = dom;
 149                v->attributes = le32_to_cpu(resp_dom->attr);
 150                strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
 151
 152                cmd = tl->tx.buf;
 153                /* ...then retrieve domain levels descriptions */
 154                do {
 155                        u32 flags;
 156                        int cnt;
 157
 158                        cmd->domain_id = cpu_to_le32(v->id);
 159                        cmd->level_index = desc_index;
 160                        ret = ph->xops->do_xfer(ph, tl);
 161                        if (ret)
 162                                break;
 163
 164                        flags = le32_to_cpu(resp_levels->flags);
 165                        num_returned = NUM_RETURNED_LEVELS(flags);
 166                        num_remaining = NUM_REMAINING_LEVELS(flags);
 167
 168                        /* Allocate space for num_levels if not already done */
 169                        if (!v->num_levels) {
 170                                ret = scmi_init_voltage_levels(dev, v,
 171                                                               num_returned,
 172                                                               num_remaining,
 173                                              SUPPORTS_SEGMENTED_LEVELS(flags));
 174                                if (ret)
 175                                        break;
 176                        }
 177
 178                        if (desc_index + num_returned > v->num_levels) {
 179                                dev_err(ph->dev,
 180                                        "No. of voltage levels can't exceed %d\n",
 181                                        v->num_levels);
 182                                ret = -EINVAL;
 183                                break;
 184                        }
 185
 186                        for (cnt = 0; cnt < num_returned; cnt++) {
 187                                s32 val;
 188
 189                                val =
 190                                    (s32)le32_to_cpu(resp_levels->voltage[cnt]);
 191                                v->levels_uv[desc_index + cnt] = val;
 192                                if (val < 0)
 193                                        v->negative_volts_allowed = true;
 194                        }
 195
 196                        desc_index += num_returned;
 197
 198                        ph->xops->reset_rx_to_maxsz(ph, tl);
 199                        /* check both to avoid infinite loop due to buggy fw */
 200                } while (num_returned && num_remaining);
 201
 202                if (ret) {
 203                        v->num_levels = 0;
 204                        devm_kfree(dev, v->levels_uv);
 205                }
 206
 207                ph->xops->reset_rx_to_maxsz(ph, td);
 208        }
 209
 210        ph->xops->xfer_put(ph, tl);
 211outd:
 212        ph->xops->xfer_put(ph, td);
 213
 214        return ret;
 215}
 216
 217static int __scmi_voltage_get_u32(const struct scmi_protocol_handle *ph,
 218                                  u8 cmd_id, u32 domain_id, u32 *value)
 219{
 220        int ret;
 221        struct scmi_xfer *t;
 222        struct voltage_info *vinfo = ph->get_priv(ph);
 223
 224        if (domain_id >= vinfo->num_domains)
 225                return -EINVAL;
 226
 227        ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(__le32), 0, &t);
 228        if (ret)
 229                return ret;
 230
 231        put_unaligned_le32(domain_id, t->tx.buf);
 232        ret = ph->xops->do_xfer(ph, t);
 233        if (!ret)
 234                *value = get_unaligned_le32(t->rx.buf);
 235
 236        ph->xops->xfer_put(ph, t);
 237        return ret;
 238}
 239
 240static int scmi_voltage_config_set(const struct scmi_protocol_handle *ph,
 241                                   u32 domain_id, u32 config)
 242{
 243        int ret;
 244        struct scmi_xfer *t;
 245        struct voltage_info *vinfo = ph->get_priv(ph);
 246        struct scmi_msg_cmd_config_set *cmd;
 247
 248        if (domain_id >= vinfo->num_domains)
 249                return -EINVAL;
 250
 251        ret = ph->xops->xfer_get_init(ph, VOLTAGE_CONFIG_SET,
 252                                     sizeof(*cmd), 0, &t);
 253        if (ret)
 254                return ret;
 255
 256        cmd = t->tx.buf;
 257        cmd->domain_id = cpu_to_le32(domain_id);
 258        cmd->config = cpu_to_le32(config & GENMASK(3, 0));
 259
 260        ret = ph->xops->do_xfer(ph, t);
 261
 262        ph->xops->xfer_put(ph, t);
 263        return ret;
 264}
 265
 266static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph,
 267                                   u32 domain_id, u32 *config)
 268{
 269        return __scmi_voltage_get_u32(ph, VOLTAGE_CONFIG_GET,
 270                                      domain_id, config);
 271}
 272
 273static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph,
 274                                  u32 domain_id, u32 flags, s32 volt_uV)
 275{
 276        int ret;
 277        struct scmi_xfer *t;
 278        struct voltage_info *vinfo = ph->get_priv(ph);
 279        struct scmi_msg_cmd_level_set *cmd;
 280
 281        if (domain_id >= vinfo->num_domains)
 282                return -EINVAL;
 283
 284        ret = ph->xops->xfer_get_init(ph, VOLTAGE_LEVEL_SET,
 285                                      sizeof(*cmd), 0, &t);
 286        if (ret)
 287                return ret;
 288
 289        cmd = t->tx.buf;
 290        cmd->domain_id = cpu_to_le32(domain_id);
 291        cmd->flags = cpu_to_le32(flags);
 292        cmd->voltage_level = cpu_to_le32(volt_uV);
 293
 294        ret = ph->xops->do_xfer(ph, t);
 295
 296        ph->xops->xfer_put(ph, t);
 297        return ret;
 298}
 299
 300static int scmi_voltage_level_get(const struct scmi_protocol_handle *ph,
 301                                  u32 domain_id, s32 *volt_uV)
 302{
 303        return __scmi_voltage_get_u32(ph, VOLTAGE_LEVEL_GET,
 304                                      domain_id, (u32 *)volt_uV);
 305}
 306
 307static const struct scmi_voltage_info * __must_check
 308scmi_voltage_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
 309{
 310        struct voltage_info *vinfo = ph->get_priv(ph);
 311
 312        if (domain_id >= vinfo->num_domains ||
 313            !vinfo->domains[domain_id].num_levels)
 314                return NULL;
 315
 316        return vinfo->domains + domain_id;
 317}
 318
 319static int scmi_voltage_domains_num_get(const struct scmi_protocol_handle *ph)
 320{
 321        struct voltage_info *vinfo = ph->get_priv(ph);
 322
 323        return vinfo->num_domains;
 324}
 325
 326static struct scmi_voltage_proto_ops voltage_proto_ops = {
 327        .num_domains_get = scmi_voltage_domains_num_get,
 328        .info_get = scmi_voltage_info_get,
 329        .config_set = scmi_voltage_config_set,
 330        .config_get = scmi_voltage_config_get,
 331        .level_set = scmi_voltage_level_set,
 332        .level_get = scmi_voltage_level_get,
 333};
 334
 335static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
 336{
 337        int ret;
 338        u32 version;
 339        struct voltage_info *vinfo;
 340
 341        ret = ph->xops->version_get(ph, &version);
 342        if (ret)
 343                return ret;
 344
 345        dev_dbg(ph->dev, "Voltage Version %d.%d\n",
 346                PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
 347
 348        vinfo = devm_kzalloc(ph->dev, sizeof(*vinfo), GFP_KERNEL);
 349        if (!vinfo)
 350                return -ENOMEM;
 351        vinfo->version = version;
 352
 353        ret = scmi_protocol_attributes_get(ph, vinfo);
 354        if (ret)
 355                return ret;
 356
 357        if (vinfo->num_domains) {
 358                vinfo->domains = devm_kcalloc(ph->dev, vinfo->num_domains,
 359                                              sizeof(*vinfo->domains),
 360                                              GFP_KERNEL);
 361                if (!vinfo->domains)
 362                        return -ENOMEM;
 363                ret = scmi_voltage_descriptors_get(ph, vinfo);
 364                if (ret)
 365                        return ret;
 366        } else {
 367                dev_warn(ph->dev, "No Voltage domains found.\n");
 368        }
 369
 370        return ph->set_priv(ph, vinfo);
 371}
 372
 373static const struct scmi_protocol scmi_voltage = {
 374        .id = SCMI_PROTOCOL_VOLTAGE,
 375        .owner = THIS_MODULE,
 376        .instance_init = &scmi_voltage_protocol_init,
 377        .ops = &voltage_proto_ops,
 378};
 379
 380DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(voltage, scmi_voltage)
 381