/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 | } |