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
scmi_smt_enable_intr(struct scmi_smt * smt,bool enable)23 static 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
33 /**
34 * Get shared memory configuration defined by the referred DT phandle
35 * Return with a errno compliant value.
36 */
scmi_dt_get_smt_buffer(struct udevice * dev,struct scmi_smt * smt)37 int 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;
42
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
51 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
57 smt->buf = devm_ioremap(dev, resource.start, smt->size);
58 if (!smt->buf)
59 return -ENOMEM;
60
61 if (device_is_compatible(dev, "arm,scmi") && ofnode_has_property(dev_ofnode(dev), "mboxes"))
62 scmi_smt_enable_intr(smt, true);
63
64 #ifdef CONFIG_ARM
65 if (dcache_status())
66 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
70 #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 */
scmi_write_msg_to_smt(struct udevice * dev,struct scmi_smt * smt,struct scmi_msg * msg)79 int 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 */
scmi_read_resp_from_smt(struct udevice * dev,struct scmi_smt * smt,struct scmi_msg * msg)116 int 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 */
scmi_clear_smt_channel(struct scmi_smt * smt)146 void 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 }
152
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 */
scmi_msg_to_smt_msg(struct udevice * dev,struct scmi_smt * smt,struct scmi_msg * msg,size_t * buf_size)157 int 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 */
scmi_msg_from_smt_msg(struct udevice * dev,struct scmi_smt * smt,struct scmi_msg * msg,size_t buf_size)188 int 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 }
203