1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/firmware/scmi/shmem.h>
8 #include <zephyr/drivers/firmware/scmi/protocol.h>
9 #include <zephyr/logging/log.h>
10 #include <string.h>
11
12 LOG_MODULE_REGISTER(arm_scmi_shmem);
13
14 #define DT_DRV_COMPAT arm_scmi_shmem
15
16 #ifndef DEVICE_MMIO_IS_IN_RAM
17 #define device_map(virt, phys, size, flags) *(virt) = (phys)
18 #endif /* DEVICE_MMIO_IS_IN_RAM */
19
20 struct scmi_shmem_config {
21 uintptr_t phys_addr;
22 uint32_t size;
23 };
24
25 struct scmi_shmem_data {
26 mm_reg_t regmap;
27 };
28
scmi_shmem_get_channel_status(const struct device * dev,uint32_t * status)29 int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status)
30 {
31 struct scmi_shmem_data *data;
32 struct scmi_shmem_layout *layout;
33
34 data = dev->data;
35 layout = (struct scmi_shmem_layout *)data->regmap;
36
37 *status = layout->chan_status;
38
39 return 0;
40 }
41
scmi_shmem_memcpy(mm_reg_t dst,mm_reg_t src,uint32_t bytes)42 static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes)
43 {
44 int i;
45
46 for (i = 0; i < bytes; i++) {
47 sys_write8(*(uint8_t *)(src + i), dst + i);
48 }
49 }
50
scmi_shmem_vendor_read_message(const struct scmi_shmem_layout * layout)51 __weak int scmi_shmem_vendor_read_message(const struct scmi_shmem_layout *layout)
52 {
53 return 0;
54 }
55
scmi_shmem_vendor_write_message(struct scmi_shmem_layout * layout)56 __weak int scmi_shmem_vendor_write_message(struct scmi_shmem_layout *layout)
57 {
58 return 0;
59 }
60
scmi_shmem_read_message(const struct device * shmem,struct scmi_message * msg)61 int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg)
62 {
63 struct scmi_shmem_layout *layout;
64 struct scmi_shmem_data *data;
65 const struct scmi_shmem_config *cfg;
66
67 data = shmem->data;
68 cfg = shmem->config;
69 layout = (struct scmi_shmem_layout *)data->regmap;
70
71 /* some sanity checks first */
72 if (!msg) {
73 return -EINVAL;
74 }
75
76 if (!msg->content && msg->len) {
77 return -EINVAL;
78 }
79
80 if (cfg->size < (sizeof(*layout) + msg->len)) {
81 LOG_ERR("message doesn't fit in shmem area");
82 return -EINVAL;
83 }
84
85 /* mismatch between expected reply size and actual size? */
86 if (msg->len != (layout->len - sizeof(layout->msg_hdr))) {
87 LOG_ERR("bad message len. Expected 0x%x, got 0x%x",
88 msg->len,
89 (uint32_t)(layout->len - sizeof(layout->msg_hdr)));
90 return -EINVAL;
91 }
92
93 /* header match? */
94 if (layout->msg_hdr != msg->hdr) {
95 LOG_ERR("bad message header. Expected 0x%x, got 0x%x",
96 msg->hdr, layout->msg_hdr);
97 return -EINVAL;
98 }
99
100 if (scmi_shmem_vendor_read_message(layout) < 0) {
101 LOG_ERR("vendor specific validation failed");
102 return -EINVAL;
103 }
104
105 if (msg->content) {
106 scmi_shmem_memcpy(POINTER_TO_UINT(msg->content),
107 data->regmap + sizeof(*layout), msg->len);
108 }
109
110 return 0;
111 }
112
scmi_shmem_write_message(const struct device * shmem,struct scmi_message * msg)113 int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *msg)
114 {
115 struct scmi_shmem_layout *layout;
116 struct scmi_shmem_data *data;
117 const struct scmi_shmem_config *cfg;
118
119 data = shmem->data;
120 cfg = shmem->config;
121 layout = (struct scmi_shmem_layout *)data->regmap;
122
123 /* some sanity checks first */
124 if (!msg) {
125 return -EINVAL;
126 }
127
128 if (!msg->content && msg->len) {
129 return -EINVAL;
130 }
131
132 if (cfg->size < (sizeof(*layout) + msg->len)) {
133 return -EINVAL;
134 }
135
136 if (!(layout->chan_status & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT)) {
137 return -EBUSY;
138 }
139
140 layout->len = sizeof(layout->msg_hdr) + msg->len;
141 layout->msg_hdr = msg->hdr;
142
143 if (msg->content) {
144 scmi_shmem_memcpy(data->regmap + sizeof(*layout),
145 POINTER_TO_UINT(msg->content), msg->len);
146 }
147
148 if (scmi_shmem_vendor_write_message(layout) < 0) {
149 return -EINVAL;
150 }
151
152 /* done, mark channel as busy and proceed */
153 layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT;
154
155 return 0;
156 }
157
scmi_shmem_channel_status(const struct device * shmem)158 uint32_t scmi_shmem_channel_status(const struct device *shmem)
159 {
160 struct scmi_shmem_layout *layout;
161 struct scmi_shmem_data *data;
162
163 data = shmem->data;
164 layout = (struct scmi_shmem_layout *)data->regmap;
165
166 return layout->chan_status;
167 }
168
scmi_shmem_update_flags(const struct device * shmem,uint32_t mask,uint32_t val)169 void scmi_shmem_update_flags(const struct device *shmem, uint32_t mask, uint32_t val)
170 {
171 struct scmi_shmem_layout *layout;
172 struct scmi_shmem_data *data;
173
174 data = shmem->data;
175 layout = (struct scmi_shmem_layout *)data->regmap;
176
177 layout->chan_flags = (layout->chan_flags & ~mask) | (val & mask);
178 }
179
scmi_shmem_init(const struct device * dev)180 static int scmi_shmem_init(const struct device *dev)
181 {
182 const struct scmi_shmem_config *cfg;
183 struct scmi_shmem_data *data;
184
185 cfg = dev->config;
186 data = dev->data;
187
188 if (cfg->size < sizeof(struct scmi_shmem_layout)) {
189 return -EINVAL;
190 }
191
192 device_map(&data->regmap, cfg->phys_addr, cfg->size, K_MEM_CACHE_NONE);
193
194 return 0;
195 }
196
197 #define SCMI_SHMEM_INIT(inst) \
198 static const struct scmi_shmem_config config_##inst = { \
199 .phys_addr = DT_INST_REG_ADDR(inst), \
200 .size = DT_INST_REG_SIZE(inst), \
201 }; \
202 \
203 static struct scmi_shmem_data data_##inst; \
204 \
205 DEVICE_DT_INST_DEFINE(inst, &scmi_shmem_init, NULL, \
206 &data_##inst, &config_##inst, \
207 PRE_KERNEL_1, \
208 CONFIG_ARM_SCMI_SHMEM_INIT_PRIORITY, \
209 NULL);
210
211 DT_INST_FOREACH_STATUS_OKAY(SCMI_SHMEM_INIT);
212