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
29 struct scmi_shmem_layout {
30 volatile uint32_t res0;
31 volatile uint32_t chan_status;
32 volatile uint32_t res1[2];
33 volatile uint32_t chan_flags;
34 volatile uint32_t len;
35 volatile uint32_t msg_hdr;
36 };
37
scmi_shmem_get_channel_status(const struct device * dev,uint32_t * status)38 int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status)
39 {
40 struct scmi_shmem_data *data;
41 struct scmi_shmem_layout *layout;
42
43 data = dev->data;
44 layout = (struct scmi_shmem_layout *)data->regmap;
45
46 *status = layout->chan_status;
47
48 return 0;
49 }
50
scmi_shmem_memcpy(mm_reg_t dst,mm_reg_t src,uint32_t bytes)51 static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes)
52 {
53 int i;
54
55 for (i = 0; i < bytes; i++) {
56 sys_write8(*(uint8_t *)(src + i), dst + i);
57 }
58 }
59
scmi_shmem_read_message(const struct device * shmem,struct scmi_message * msg)60 int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg)
61 {
62 struct scmi_shmem_layout *layout;
63 struct scmi_shmem_data *data;
64 const struct scmi_shmem_config *cfg;
65
66 data = shmem->data;
67 cfg = shmem->config;
68 layout = (struct scmi_shmem_layout *)data->regmap;
69
70 /* some sanity checks first */
71 if (!msg) {
72 return -EINVAL;
73 }
74
75 if (!msg->content && msg->len) {
76 return -EINVAL;
77 }
78
79 if (cfg->size < (sizeof(*layout) + msg->len)) {
80 LOG_ERR("message doesn't fit in shmem area");
81 return -EINVAL;
82 }
83
84 /* mismatch between expected reply size and actual size? */
85 if (msg->len != (layout->len - sizeof(layout->msg_hdr))) {
86 LOG_ERR("bad message len. Expected 0x%x, got 0x%x",
87 msg->len,
88 (uint32_t)(layout->len - sizeof(layout->msg_hdr)));
89 return -EINVAL;
90 }
91
92 /* header match? */
93 if (layout->msg_hdr != msg->hdr) {
94 LOG_ERR("bad message header. Expected 0x%x, got 0x%x",
95 msg->hdr, layout->msg_hdr);
96 return -EINVAL;
97 }
98
99 if (msg->content) {
100 scmi_shmem_memcpy(POINTER_TO_UINT(msg->content),
101 data->regmap + sizeof(*layout), msg->len);
102 }
103
104 return 0;
105 }
106
scmi_shmem_write_message(const struct device * shmem,struct scmi_message * msg)107 int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *msg)
108 {
109 struct scmi_shmem_layout *layout;
110 struct scmi_shmem_data *data;
111 const struct scmi_shmem_config *cfg;
112
113 data = shmem->data;
114 cfg = shmem->config;
115 layout = (struct scmi_shmem_layout *)data->regmap;
116
117 /* some sanity checks first */
118 if (!msg) {
119 return -EINVAL;
120 }
121
122 if (!msg->content && msg->len) {
123 return -EINVAL;
124 }
125
126 if (cfg->size < (sizeof(*layout) + msg->len)) {
127 return -EINVAL;
128 }
129
130 if (!(layout->chan_status & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT)) {
131 return -EBUSY;
132 }
133
134 layout->len = sizeof(layout->msg_hdr) + msg->len;
135 layout->msg_hdr = msg->hdr;
136
137 if (msg->content) {
138 scmi_shmem_memcpy(data->regmap + sizeof(*layout),
139 POINTER_TO_UINT(msg->content), msg->len);
140 }
141
142 /* done, mark channel as busy and proceed */
143 layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT;
144
145 return 0;
146 }
147
scmi_shmem_channel_status(const struct device * shmem)148 uint32_t scmi_shmem_channel_status(const struct device *shmem)
149 {
150 struct scmi_shmem_layout *layout;
151 struct scmi_shmem_data *data;
152
153 data = shmem->data;
154 layout = (struct scmi_shmem_layout *)data->regmap;
155
156 return layout->chan_status;
157 }
158
scmi_shmem_update_flags(const struct device * shmem,uint32_t mask,uint32_t val)159 void scmi_shmem_update_flags(const struct device *shmem, uint32_t mask, uint32_t val)
160 {
161 struct scmi_shmem_layout *layout;
162 struct scmi_shmem_data *data;
163
164 data = shmem->data;
165 layout = (struct scmi_shmem_layout *)data->regmap;
166
167 layout->chan_flags = (layout->chan_flags & ~mask) | (val & mask);
168 }
169
scmi_shmem_init(const struct device * dev)170 static int scmi_shmem_init(const struct device *dev)
171 {
172 const struct scmi_shmem_config *cfg;
173 struct scmi_shmem_data *data;
174
175 cfg = dev->config;
176 data = dev->data;
177
178 if (cfg->size < sizeof(struct scmi_shmem_layout)) {
179 return -EINVAL;
180 }
181
182 device_map(&data->regmap, cfg->phys_addr, cfg->size, K_MEM_CACHE_NONE);
183
184 return 0;
185 }
186
187 #define SCMI_SHMEM_INIT(inst) \
188 static const struct scmi_shmem_config config_##inst = { \
189 .phys_addr = DT_INST_REG_ADDR(inst), \
190 .size = DT_INST_REG_SIZE(inst), \
191 }; \
192 \
193 static struct scmi_shmem_data data_##inst; \
194 \
195 DEVICE_DT_INST_DEFINE(inst, &scmi_shmem_init, NULL, \
196 &data_##inst, &config_##inst, \
197 PRE_KERNEL_1, \
198 CONFIG_ARM_SCMI_SHMEM_INIT_PRIORITY, \
199 NULL);
200
201 DT_INST_FOREACH_STATUS_OKAY(SCMI_SHMEM_INIT);
202