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