master
c 303 lines 8.42 KB
Raw
1 /*
2 * Mock I3C Device
3 *
4 * Copyright (c) 2025 Google LLC
5 *
6 * The mock I3C device can be thought of as a simple EEPROM. It has a buffer,
7 * and the pointer in the buffer is reset to 0 on an I3C STOP.
8 * To write to the buffer, issue a private write and send data.
9 * To read from the buffer, issue a private read.
10 *
11 * The mock target also supports sending target interrupt IBIs.
12 * To issue an IBI, set the 'ibi-magic-num' property to a non-zero number, and
13 * send that number in a private transaction. The mock target will issue an IBI
14 * after 1 second.
15 *
16 * It also supports a handful of CCCs that are typically used when probing I3C
17 * devices.
18 *
19 * SPDX-License-Identifier: GPL-2.0-or-later
20 */
21
22 #include "qemu/osdep.h"
23 #include "qemu/log.h"
24 #include "trace.h"
25 #include "hw/i3c/i3c.h"
26 #include "hw/i3c/mock-i3c-target.h"
27 #include "hw/core/irq.h"
28 #include "hw/core/qdev-properties.h"
29 #include "qapi/error.h"
30 #include "qemu/module.h"
31
32 #define IBI_DELAY_NS (1 * 1000 * 1000)
33
34 static uint32_t mock_i3c_target_rx(I3CTarget *i3c, uint8_t *data,
35 uint32_t num_to_read)
36 {
37 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
38 uint32_t i;
39
40 /* Bounds check. */
41 if (s->p_buf == s->cfg.buf_size) {
42 return 0;
43 }
44
45 for (i = 0; i < num_to_read; i++) {
46 data[i] = s->buf[s->p_buf];
47 trace_mock_i3c_target_rx(data[i]);
48 s->p_buf++;
49 if (s->p_buf == s->cfg.buf_size) {
50 break;
51 }
52 }
53
54 /* Return the number of bytes we're sending to the controller. */
55 return i;
56 }
57
58 static void mock_i3c_target_ibi_timer_start(MockI3cTargetState *s)
59 {
60 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
61 timer_mod(&s->qtimer, now + IBI_DELAY_NS);
62 }
63
64 static int mock_i3c_target_tx(I3CTarget *i3c, const uint8_t *data,
65 uint32_t num_to_send, uint32_t *num_sent)
66 {
67 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
68 int ret;
69 uint32_t to_write;
70
71 if (s->cfg.ibi_magic && num_to_send == 1 && s->cfg.ibi_magic == *data) {
72 mock_i3c_target_ibi_timer_start(s);
73 *num_sent = 1;
74 return 0;
75 }
76
77 /* Bounds check. */
78 if (num_to_send + s->p_buf > s->cfg.buf_size) {
79 to_write = s->cfg.buf_size - s->p_buf;
80 ret = -1;
81 } else {
82 to_write = num_to_send;
83 ret = 0;
84 }
85 for (uint32_t i = 0; i < to_write; i++) {
86 trace_mock_i3c_target_tx(data[i]);
87 s->buf[s->p_buf] = data[i];
88 s->p_buf++;
89 }
90 *num_sent = to_write;
91 return ret;
92 }
93
94 static int mock_i3c_target_event(I3CTarget *i3c, enum I3CEvent event)
95 {
96 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
97
98 trace_mock_i3c_target_event(event);
99 if (event == I3C_STOP) {
100 s->in_ccc = false;
101 s->curr_ccc = 0;
102 s->ccc_byte_offset = 0;
103 s->p_buf = 0;
104 }
105
106 return 0;
107 }
108
109 static int mock_i3c_target_handle_ccc_read(I3CTarget *i3c, uint8_t *data,
110 uint32_t num_to_read,
111 uint32_t *num_read)
112 {
113 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
114
115 switch (s->curr_ccc) {
116 case I3C_CCCD_GETMXDS:
117 /* Default data rate for I3C. */
118 while (s->ccc_byte_offset < num_to_read) {
119 if (s->ccc_byte_offset >= 2) {
120 break;
121 }
122 data[s->ccc_byte_offset] = 0;
123 *num_read = s->ccc_byte_offset;
124 s->ccc_byte_offset++;
125 }
126 break;
127 case I3C_CCCD_GETCAPS:
128 /* Support I3C version 1.1.x, no other features. */
129 while (s->ccc_byte_offset < num_to_read) {
130 if (s->ccc_byte_offset >= 2) {
131 break;
132 }
133 if (s->ccc_byte_offset == 0) {
134 data[s->ccc_byte_offset] = 0;
135 } else {
136 data[s->ccc_byte_offset] = 0x01;
137 }
138 *num_read = s->ccc_byte_offset;
139 s->ccc_byte_offset++;
140 }
141 break;
142 case I3C_CCCD_GETMWL:
143 case I3C_CCCD_GETMRL:
144 /* MWL/MRL is MSB first. */
145 while (s->ccc_byte_offset < num_to_read) {
146 if (s->ccc_byte_offset >= 2) {
147 break;
148 }
149 if (s->ccc_byte_offset == 0) {
150 data[s->ccc_byte_offset] = (uint8_t)(s->cfg.buf_size >> 8);
151 } else {
152 data[s->ccc_byte_offset] = (uint8_t)s->cfg.buf_size;
153 }
154
155 s->ccc_byte_offset++;
156 *num_read = num_to_read;
157 }
158 break;
159 case I3C_CCC_ENTDAA:
160 case I3C_CCCD_GETPID:
161 case I3C_CCCD_GETBCR:
162 case I3C_CCCD_GETDCR:
163 /* Nothing to do. */
164 break;
165 default:
166 qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc);
167 return -1;
168 }
169
170 trace_mock_i3c_target_handle_ccc_read(*num_read, num_to_read);
171 return 0;
172 }
173
174 static int mock_i3c_target_handle_ccc_write(I3CTarget *i3c, const uint8_t *data,
175 uint32_t num_to_send,
176 uint32_t *num_sent)
177 {
178 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
179
180 if (!s->curr_ccc) {
181 s->in_ccc = true;
182 s->curr_ccc = *data;
183 trace_mock_i3c_target_new_ccc(s->curr_ccc);
184 }
185
186 *num_sent = 1;
187 switch (s->curr_ccc) {
188 case I3C_CCC_ENEC:
189 case I3C_CCCD_ENEC:
190 s->can_ibi = true;
191 break;
192 case I3C_CCC_DISEC:
193 case I3C_CCCD_DISEC:
194 s->can_ibi = false;
195 break;
196 case I3C_CCC_ENTDAA:
197 case I3C_CCC_SETAASA:
198 case I3C_CCC_RSTDAA:
199 case I3C_CCCD_SETDASA:
200 case I3C_CCCD_GETPID:
201 case I3C_CCCD_GETBCR:
202 case I3C_CCCD_GETDCR:
203 case I3C_CCCD_GETMWL:
204 case I3C_CCCD_GETMRL:
205 case I3C_CCCD_GETMXDS:
206 case I3C_CCCD_GETCAPS:
207 /* Nothing to do. */
208 break;
209 default:
210 qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc);
211 return -1;
212 }
213
214 trace_mock_i3c_target_handle_ccc_write(*num_sent, num_to_send);
215 return 0;
216 }
217
218 static void mock_i3c_target_do_ibi(MockI3cTargetState *s)
219 {
220 if (!s->can_ibi) {
221 return;
222 }
223
224 trace_mock_i3c_target_do_ibi(s->parent_obj.address, true);
225 int nack = i3c_target_send_ibi(&s->parent_obj, s->parent_obj.address,
226 /*is_recv=*/true);
227 /* Getting NACKed isn't necessarily an error, just print it out. */
228 if (nack) {
229 trace_mock_i3c_target_do_ibi_nack("sending");
230 }
231 nack = i3c_target_ibi_finish(&s->parent_obj, 0x00);
232 if (nack) {
233 trace_mock_i3c_target_do_ibi_nack("finishing");
234 }
235 }
236
237 static void mock_i3c_target_timer_elapsed(void *opaque)
238 {
239 MockI3cTargetState *s = MOCK_I3C_TARGET(opaque);
240 timer_del(&s->qtimer);
241 mock_i3c_target_do_ibi(s);
242 }
243
244 static void mock_i3c_target_reset(I3CTarget *i3c)
245 {
246 MockI3cTargetState *s = MOCK_I3C_TARGET(i3c);
247 s->can_ibi = false;
248 }
249
250 static void mock_i3c_target_realize(DeviceState *dev, Error **errp)
251 {
252 MockI3cTargetState *s = MOCK_I3C_TARGET(dev);
253 s->buf = g_new0(uint8_t, s->cfg.buf_size);
254 mock_i3c_target_reset(&s->parent_obj);
255 }
256
257 static void mock_i3c_target_init(Object *obj)
258 {
259 MockI3cTargetState *s = MOCK_I3C_TARGET(obj);
260 s->can_ibi = false;
261
262 /* For IBIs. */
263 timer_init_ns(&s->qtimer, QEMU_CLOCK_VIRTUAL, mock_i3c_target_timer_elapsed,
264 s);
265 }
266
267 static const Property remote_i3c_props[] = {
268 /* The size of the internal buffer. */
269 DEFINE_PROP_UINT32("buf-size", MockI3cTargetState, cfg.buf_size, 0x100),
270 /*
271 * If the mock target receives this number, it will issue an IBI after
272 * 1 second. Disabled if the IBI magic number is 0.
273 */
274 DEFINE_PROP_UINT8("ibi-magic-num", MockI3cTargetState, cfg.ibi_magic, 0x00),
275 };
276
277 static void mock_i3c_target_class_init(ObjectClass *klass, const void *data)
278 {
279 DeviceClass *dc = DEVICE_CLASS(klass);
280 I3CTargetClass *k = I3C_TARGET_CLASS(klass);
281
282 dc->realize = mock_i3c_target_realize;
283 k->event = mock_i3c_target_event;
284 k->recv = mock_i3c_target_rx;
285 k->send = mock_i3c_target_tx;
286 k->handle_ccc_read = mock_i3c_target_handle_ccc_read;
287 k->handle_ccc_write = mock_i3c_target_handle_ccc_write;
288
289 device_class_set_props(dc, remote_i3c_props);
290 }
291
292 static const TypeInfo mock_i3c_target_types[] = {
293 {
294 .name = TYPE_MOCK_I3C_TARGET,
295 .parent = TYPE_I3C_TARGET,
296 .instance_size = sizeof(MockI3cTargetState),
297 .instance_init = mock_i3c_target_init,
298 .class_init = mock_i3c_target_class_init,
299 },
300 };
301
302 DEFINE_TYPES(mock_i3c_target_types)
303