blob: 3253f4211d624c8a1d35e803636189d20830f8ed [file] [log] [blame]
Etienne Carriere2b412082020-09-09 18:44:01 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
Etienne Carrierec624c972022-05-31 18:09:16 +02004 * Copyright (C) 2019-2022 Linaro Limited.
Etienne Carriere2b412082020-09-09 18:44:01 +02005 */
6
Patrick Delaunay5dd84d42021-02-24 11:19:44 +01007#define LOG_CATEGORY UCLASS_SCMI_AGENT
8
Etienne Carriere2b412082020-09-09 18:44:01 +02009#include <cpu_func.h>
10#include <dm.h>
Sean Anderson31786fe2020-10-04 21:39:44 -040011#include <dm/device_compat.h>
Etienne Carriere2b412082020-09-09 18:44:01 +020012#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
Viorel Sumanf842d4d2025-04-01 15:56:35 +080023static void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable)
24{
25 struct scmi_smt_header *hdr = (void *)smt->buf;
26
27 if (enable)
28 hdr->flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
29 else
30 hdr->flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED;
31}
32
Etienne Carriere2b412082020-09-09 18:44:01 +020033/**
34 * Get shared memory configuration defined by the referred DT phandle
35 * Return with a errno compliant value.
36 */
37int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt)
38{
39 int ret;
40 struct ofnode_phandle_args args;
41 struct resource resource;
Etienne Carriere2b412082020-09-09 18:44:01 +020042
43 ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args);
44 if (ret)
45 return ret;
46
47 ret = ofnode_read_resource(args.node, 0, &resource);
48 if (ret)
49 return ret;
50
Etienne Carriere2b412082020-09-09 18:44:01 +020051 smt->size = resource_size(&resource);
52 if (smt->size < sizeof(struct scmi_smt_header)) {
53 dev_err(dev, "Shared memory buffer too small\n");
54 return -EINVAL;
55 }
56
Patrick Delaunay2a28c6e2021-04-06 09:38:06 +020057 smt->buf = devm_ioremap(dev, resource.start, smt->size);
Etienne Carriere2b412082020-09-09 18:44:01 +020058 if (!smt->buf)
59 return -ENOMEM;
60
Viorel Sumanf842d4d2025-04-01 15:56:35 +080061 if (device_is_compatible(dev, "arm,scmi") && ofnode_has_property(dev_ofnode(dev), "mboxes"))
62 scmi_smt_enable_intr(smt, true);
63
Etienne Carriere2b412082020-09-09 18:44:01 +020064#ifdef CONFIG_ARM
65 if (dcache_status())
Patrick Delaunayab5154f2021-03-16 09:29:40 +010066 mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE),
67 ALIGN(smt->size, MMU_SECTION_SIZE),
68 DCACHE_OFF);
69
Etienne Carriere2b412082020-09-09 18:44:01 +020070#endif
71
72 return 0;
73}
74
75/**
76 * Write SCMI message @msg into a SMT shared buffer @smt.
77 * Return 0 on success and with a negative errno in case of error.
78 */
79int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
80 struct scmi_msg *msg)
81{
82 struct scmi_smt_header *hdr = (void *)smt->buf;
83
84 if ((!msg->in_msg && msg->in_msg_sz) ||
85 (!msg->out_msg && msg->out_msg_sz))
86 return -EINVAL;
87
88 if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
89 dev_dbg(dev, "Channel busy\n");
90 return -EBUSY;
91 }
92
93 if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
94 smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
95 dev_dbg(dev, "Buffer too small\n");
96 return -ETOOSMALL;
97 }
98
99 /* Load message in shared memory */
100 hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
101 hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header);
102 hdr->msg_header = SMT_HEADER_TOKEN(0) |
103 SMT_HEADER_MESSAGE_TYPE(0) |
104 SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
105 SMT_HEADER_MESSAGE_ID(msg->message_id);
106
107 memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
108
109 return 0;
110}
111
112/**
113 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
114 * Return 0 on success and with a negative errno in case of error.
115 */
116int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
117 struct scmi_msg *msg)
118{
119 struct scmi_smt_header *hdr = (void *)smt->buf;
120
121 if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
122 dev_err(dev, "Channel unexpectedly busy\n");
123 return -EBUSY;
124 }
125
126 if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) {
127 dev_err(dev, "Channel error reported, reset channel\n");
128 return -ECOMM;
129 }
130
131 if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) {
132 dev_err(dev, "Buffer to small\n");
133 return -ETOOSMALL;
134 }
135
136 /* Get the data */
137 msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header);
138 memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
139
140 return 0;
141}
142
143/**
144 * Clear SMT flags in shared buffer to allow further message exchange
145 */
146void scmi_clear_smt_channel(struct scmi_smt *smt)
147{
148 struct scmi_smt_header *hdr = (void *)smt->buf;
149
150 hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
151}
Etienne Carrierec624c972022-05-31 18:09:16 +0200152
153/**
154 * Write SCMI message @msg into a SMT_MSG shared buffer @smt.
155 * Return 0 on success and with a negative errno in case of error.
156 */
157int scmi_msg_to_smt_msg(struct udevice *dev, struct scmi_smt *smt,
158 struct scmi_msg *msg, size_t *buf_size)
159{
160 struct scmi_smt_msg_header *hdr = (void *)smt->buf;
161
162 if ((!msg->in_msg && msg->in_msg_sz) ||
163 (!msg->out_msg && msg->out_msg_sz))
164 return -EINVAL;
165
166 if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
167 smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
168 dev_dbg(dev, "Buffer too small\n");
169 return -ETOOSMALL;
170 }
171
172 *buf_size = msg->in_msg_sz + sizeof(hdr->msg_header);
173
174 hdr->msg_header = SMT_HEADER_TOKEN(0) |
175 SMT_HEADER_MESSAGE_TYPE(0) |
176 SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
177 SMT_HEADER_MESSAGE_ID(msg->message_id);
178
179 memcpy(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
180
181 return 0;
182}
183
184/**
185 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
186 * Return 0 on success and with a negative errno in case of error.
187 */
188int scmi_msg_from_smt_msg(struct udevice *dev, struct scmi_smt *smt,
189 struct scmi_msg *msg, size_t buf_size)
190{
191 struct scmi_smt_msg_header *hdr = (void *)smt->buf;
192
193 if (buf_size > msg->out_msg_sz + sizeof(hdr->msg_header)) {
194 dev_err(dev, "Buffer to small\n");
195 return -ETOOSMALL;
196 }
197
198 msg->out_msg_sz = buf_size - sizeof(hdr->msg_header);
199 memcpy(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
200
201 return 0;
202}