uboot/drivers/firmware/scmi/smccc_agent.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2020 Linaro Limited.
   4 */
   5
   6#define LOG_CATEGORY UCLASS_SCMI_AGENT
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <errno.h>
  11#include <scmi_agent.h>
  12#include <scmi_agent-uclass.h>
  13#include <dm/devres.h>
  14#include <dm/device_compat.h>
  15#include <dm/device-internal.h>
  16#include <linux/arm-smccc.h>
  17#include <linux/compat.h>
  18
  19#include "smt.h"
  20
  21#define SMCCC_RET_NOT_SUPPORTED         ((unsigned long)-1)
  22
  23/**
  24 * struct scmi_smccc_channel - Description of an SCMI SMCCC transport
  25 * @func_id:    SMCCC function ID used by the SCMI transport
  26 * @smt:        Shared memory buffer
  27 */
  28struct scmi_smccc_channel {
  29        ulong func_id;
  30        struct scmi_smt smt;
  31};
  32
  33/**
  34 * struct scmi_channel - Channel instance referenced in SCMI drivers
  35 * @ref: Reference to local channel instance
  36 **/
  37struct scmi_channel {
  38        struct scmi_smccc_channel ref;
  39};
  40
  41static int scmi_smccc_process_msg(struct udevice *dev,
  42                                  struct scmi_channel *channel,
  43                                  struct scmi_msg *msg)
  44{
  45        struct scmi_smccc_channel *chan = &channel->ref;
  46        struct arm_smccc_res res;
  47        int ret;
  48
  49        ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
  50        if (ret)
  51                return ret;
  52
  53        arm_smccc_smc(chan->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
  54        if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
  55                ret = -ENXIO;
  56        else
  57                ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
  58
  59        scmi_clear_smt_channel(&chan->smt);
  60
  61        return ret;
  62}
  63
  64static int setup_channel(struct udevice *dev, struct scmi_smccc_channel *chan)
  65{
  66        u32 func_id;
  67        int ret;
  68
  69        if (dev_read_u32(dev, "arm,smc-id", &func_id)) {
  70                dev_err(dev, "Missing property func-id\n");
  71                return -EINVAL;
  72        }
  73
  74        chan->func_id = func_id;
  75
  76        ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
  77        if (ret)
  78                dev_err(dev, "Failed to get smt resources: %d\n", ret);
  79
  80        return ret;
  81}
  82
  83static int scmi_smccc_get_channel(struct udevice *dev,
  84                                  struct scmi_channel **channel)
  85{
  86        struct scmi_smccc_channel *base_chan = dev_get_plat(dev);
  87        struct scmi_smccc_channel *chan;
  88        u32 func_id;
  89        int ret;
  90
  91        if (dev_read_u32(dev, "arm,smc-id", &func_id)) {
  92                /* Uses agent base channel */
  93                *channel = container_of(base_chan, struct scmi_channel, ref);
  94
  95                return 0;
  96        }
  97
  98        /* Setup a dedicated channel */
  99        chan = calloc(1, sizeof(*chan));
 100        if (!chan)
 101                return -ENOMEM;
 102
 103        ret = setup_channel(dev, chan);
 104        if (ret) {
 105                free(chan);
 106                return ret;
 107        }
 108
 109        *channel = container_of(chan, struct scmi_channel, ref);
 110
 111        return 0;
 112}
 113
 114static int scmi_smccc_of_to_plat(struct udevice *dev)
 115{
 116        struct scmi_smccc_channel *chan = dev_get_plat(dev);
 117
 118        return setup_channel(dev, chan);
 119}
 120
 121static const struct udevice_id scmi_smccc_ids[] = {
 122        { .compatible = "arm,scmi-smc" },
 123        { }
 124};
 125
 126static const struct scmi_agent_ops scmi_smccc_ops = {
 127        .of_get_channel = scmi_smccc_get_channel,
 128        .process_msg = scmi_smccc_process_msg,
 129};
 130
 131U_BOOT_DRIVER(scmi_smccc) = {
 132        .name           = "scmi-over-smccc",
 133        .id             = UCLASS_SCMI_AGENT,
 134        .of_match       = scmi_smccc_ids,
 135        .plat_auto      = sizeof(struct scmi_smccc_channel),
 136        .of_to_plat     = scmi_smccc_of_to_plat,
 137        .ops            = &scmi_smccc_ops,
 138};
 139