Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/drivers/firmware/scmi/smt.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
 * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4
 * Copyright (C) 2019-2022 Linaro Limited.
5
 */
6
7
#define LOG_CATEGORY UCLASS_SCMI_AGENT
8
9
#include <cpu_func.h>
10
#include <dm.h>
11
#include <dm/device_compat.h>
12
#include <errno.h>
13
#include <scmi_agent.h>
14
#include <asm/cache.h>
15
#include <asm/system.h>
16
#include <dm/ofnode.h>
17
#include <linux/compat.h>
18
#include <linux/io.h>
19
#include <linux/ioport.h>
20
21
#include "smt.h"
22
23
static void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable)
24
0
{
25
0
  struct scmi_smt_header *hdr = (void *)smt->buf;
26
27
0
  if (enable)
28
0
    iowrite32(ioread32(&hdr->flags) | SCMI_SHMEM_FLAG_INTR_ENABLED, &hdr->flags);
29
0
  else
30
0
    iowrite32(ioread32(&hdr->flags) & ~SCMI_SHMEM_FLAG_INTR_ENABLED, &hdr->flags);
31
0
}
32
33
/**
34
 * Get shared memory configuration defined by the referred DT phandle
35
 * Return with a errno compliant value.
36
 */
37
int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt)
38
0
{
39
0
  int ret;
40
0
  struct ofnode_phandle_args args;
41
0
  struct resource resource;
42
43
0
  ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args);
44
0
  if (ret)
45
0
    return ret;
46
47
0
  ret = ofnode_read_resource(args.node, 0, &resource);
48
0
  if (ret)
49
0
    return ret;
50
51
0
  smt->size = resource_size(&resource);
52
0
  if (smt->size < sizeof(struct scmi_smt_header)) {
53
0
    dev_err(dev, "Shared memory buffer too small\n");
54
0
    return -EINVAL;
55
0
  }
56
57
0
  smt->buf = devm_ioremap(dev, resource.start, smt->size);
58
0
  if (!smt->buf)
59
0
    return -ENOMEM;
60
61
0
  if (device_is_compatible(dev, "arm,scmi") && ofnode_has_property(dev_ofnode(dev), "mboxes"))
62
0
    scmi_smt_enable_intr(smt, true);
63
64
0
  return 0;
65
0
}
66
67
/**
68
 * Write SCMI message @msg into a SMT shared buffer @smt.
69
 * Return 0 on success and with a negative errno in case of error.
70
 */
71
int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
72
        struct scmi_msg *msg)
73
0
{
74
0
  struct scmi_smt_header *hdr = (void *)smt->buf;
75
76
0
  if ((!msg->in_msg && msg->in_msg_sz) ||
77
0
      (!msg->out_msg && msg->out_msg_sz))
78
0
    return -EINVAL;
79
80
0
  if (!(ioread32(&hdr->channel_status) & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
81
0
    dev_dbg(dev, "Channel busy\n");
82
0
    return -EBUSY;
83
0
  }
84
85
0
  if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
86
0
      smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
87
0
    dev_err(dev,
88
0
      "Buffer write too small: mst->size:%zu, in_msg_sz:%zu, out_msg_sz:%zu\n",
89
0
      smt->size, msg->in_msg_sz, msg->out_msg_sz);
90
0
    return -ETOOSMALL;
91
0
  }
92
93
  /* Load message in shared memory */
94
0
  iowrite32(ioread32(&hdr->channel_status) & ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE,
95
0
      &hdr->channel_status);
96
0
  iowrite32(msg->in_msg_sz + sizeof(hdr->msg_header), &hdr->length);
97
0
  iowrite32(SMT_HEADER_TOKEN(0) |
98
0
      SMT_HEADER_MESSAGE_TYPE(0) |
99
0
      SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
100
0
      SMT_HEADER_MESSAGE_ID(msg->message_id), &hdr->msg_header);
101
102
0
  memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
103
104
0
  return 0;
105
0
}
106
107
/**
108
 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
109
 * Return 0 on success and with a negative errno in case of error.
110
 */
111
int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
112
          struct scmi_msg *msg)
113
0
{
114
0
  struct scmi_smt_header *hdr = (void *)smt->buf;
115
116
0
  if (!(ioread32(&hdr->channel_status) & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
117
0
    dev_err(dev, "Channel unexpectedly busy\n");
118
0
    return -EBUSY;
119
0
  }
120
121
0
  if (ioread32(&hdr->channel_status) & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) {
122
0
    dev_err(dev, "Channel error reported, reset channel\n");
123
0
    return -ECOMM;
124
0
  }
125
126
0
  if (ioread32(&hdr->length) > msg->out_msg_sz + sizeof(hdr->msg_header)) {
127
0
    dev_err(dev, "Buffer too small: hdr->length:%u, out_msg_sz:%zu\n",
128
0
      ioread32(&hdr->length), msg->out_msg_sz);
129
0
    return -ETOOSMALL;
130
0
  }
131
132
  /* Get the data */
133
0
  msg->out_msg_sz = ioread32(&hdr->length) - sizeof(hdr->msg_header);
134
0
  memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
135
136
0
  return 0;
137
0
}
138
139
/**
140
 * Clear SMT flags in shared buffer to allow further message exchange
141
 */
142
void scmi_clear_smt_channel(struct scmi_smt *smt)
143
0
{
144
0
  struct scmi_smt_header *hdr = (void *)smt->buf;
145
146
0
  iowrite32(ioread32(&hdr->channel_status) & ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR,
147
0
      &hdr->channel_status);
148
0
}
149
150
/**
151
 * Write SCMI message @msg into a SMT_MSG shared buffer @smt.
152
 * Return 0 on success and with a negative errno in case of error.
153
 */
154
int scmi_msg_to_smt_msg(struct udevice *dev, struct scmi_smt *smt,
155
      struct scmi_msg *msg, size_t *buf_size)
156
0
{
157
0
  struct scmi_smt_msg_header *hdr = (void *)smt->buf;
158
159
0
  if ((!msg->in_msg && msg->in_msg_sz) ||
160
0
      (!msg->out_msg && msg->out_msg_sz))
161
0
    return -EINVAL;
162
163
0
  if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
164
0
      smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
165
0
    dev_err(dev, "Buffer too small: mst->size:%zu, in_msg_sz:%zu, out_msg_sz:%zu\n",
166
0
      smt->size, msg->in_msg_sz, msg->out_msg_sz);
167
0
    return -ETOOSMALL;
168
0
  }
169
170
0
  *buf_size = msg->in_msg_sz + sizeof(hdr->msg_header);
171
172
0
  iowrite32(SMT_HEADER_TOKEN(0) |
173
0
      SMT_HEADER_MESSAGE_TYPE(0) |
174
0
      SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
175
0
      SMT_HEADER_MESSAGE_ID(msg->message_id), &hdr->msg_header);
176
177
0
  memcpy_fromio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
178
179
0
  return 0;
180
0
}
181
182
/**
183
 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
184
 * Return 0 on success and with a negative errno in case of error.
185
 */
186
int scmi_msg_from_smt_msg(struct udevice *dev, struct scmi_smt *smt,
187
        struct scmi_msg *msg, size_t buf_size)
188
0
{
189
0
  struct scmi_smt_msg_header *hdr = (void *)smt->buf;
190
191
0
  if (buf_size > msg->out_msg_sz + sizeof(hdr->msg_header)) {
192
0
    dev_err(dev, "Buffer too small: buf_size:%zu, out_msg_sz:%zu\n",
193
0
      buf_size, msg->out_msg_sz);
194
0
    return -ETOOSMALL;
195
0
  }
196
197
0
  msg->out_msg_sz = buf_size - sizeof(hdr->msg_header);
198
0
  memcpy_toio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
199
200
0
  return 0;
201
0
}