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