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