xref: /openbmc/qemu/hw/i3c/mock-target.c (revision c6fd2f5f)
1 /*
2  * Mock I3C Device
3  *
4  * Copyright (c) 2023 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  * This program is free software; you can redistribute it and/or modify it
20  * under the terms of the GNU General Public License as published by the
21  * Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful, but WITHOUT
25  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27  * for more details.
28  */
29 
30 #include "qemu/osdep.h"
31 #include "qemu/log.h"
32 #include "trace.h"
33 #include "hw/i3c/i3c.h"
34 #include "hw/i3c/mock-target.h"
35 #include "hw/irq.h"
36 #include "hw/qdev-properties.h"
37 #include "qapi/error.h"
38 #include "qemu/module.h"
39 
40 #ifndef MOCK_TARGET_DEBUG
41 #define MOCK_TARGET_DEBUG 0
42 #endif
43 
44 #define DB_PRINTF(...) do { \
45         if (MOCK_TARGET_DEBUG) { \
46             qemu_log("%s: ", __func__); \
47             qemu_log(__VA_ARGS__); \
48         } \
49     } while (0)
50 
51 #define IBI_DELAY_NS (1 * 1000 * 1000)
52 
mock_target_rx(I3CTarget * i3c,uint8_t * data,uint32_t num_to_read)53 static uint32_t mock_target_rx(I3CTarget *i3c, uint8_t *data,
54                                uint32_t num_to_read)
55 {
56     MockTargetState *s = MOCK_TARGET(i3c);
57     uint32_t i;
58 
59     /* Bounds check. */
60     if (s->p_buf == s->cfg.buf_size) {
61         return 0;
62     }
63 
64     for (i = 0; i < num_to_read; i++) {
65         data[i] = s->buf[s->p_buf];
66         trace_mock_target_rx(data[i]);
67         s->p_buf++;
68         if (s->p_buf == s->cfg.buf_size) {
69             break;
70         }
71     }
72 
73     /* Return the number of bytes we're sending to the controller. */
74     return i;
75 }
76 
mock_target_ibi_timer_start(MockTargetState * s)77 static void mock_target_ibi_timer_start(MockTargetState *s)
78 {
79     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
80     timer_mod(&s->qtimer, now + IBI_DELAY_NS);
81 }
82 
mock_target_tx(I3CTarget * i3c,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)83 static int mock_target_tx(I3CTarget *i3c, const uint8_t *data,
84                           uint32_t num_to_send, uint32_t *num_sent)
85 {
86     MockTargetState *s = MOCK_TARGET(i3c);
87     int ret;
88     uint32_t to_write;
89 
90     if (s->cfg.ibi_magic && num_to_send == 1 && s->cfg.ibi_magic == *data) {
91         mock_target_ibi_timer_start(s);
92         return 0;
93     }
94 
95     /* Bounds check. */
96     if (num_to_send + s->p_buf > s->cfg.buf_size) {
97         to_write = s->cfg.buf_size - s->p_buf;
98         ret = -1;
99     } else {
100         to_write = num_to_send;
101         ret = 0;
102     }
103     for (uint32_t i = 0; i < to_write; i++) {
104         trace_mock_target_tx(data[i]);
105         s->buf[s->p_buf] = data[i];
106         s->p_buf++;
107     }
108     return ret;
109 }
110 
mock_target_event(I3CTarget * i3c,enum I3CEvent event)111 static int mock_target_event(I3CTarget *i3c, enum I3CEvent event)
112 {
113     MockTargetState *s = MOCK_TARGET(i3c);
114 
115     trace_mock_target_event(event);
116     if (event == I3C_STOP) {
117         s->in_ccc = false;
118         s->curr_ccc = 0;
119         s->ccc_byte_offset = 0;
120         s->p_buf = 0;
121     }
122 
123     return 0;
124 }
125 
mock_target_handle_ccc_read(I3CTarget * i3c,uint8_t * data,uint32_t num_to_read,uint32_t * num_read)126 static int mock_target_handle_ccc_read(I3CTarget *i3c, uint8_t *data,
127                                        uint32_t num_to_read, uint32_t *num_read)
128 {
129     MockTargetState *s = MOCK_TARGET(i3c);
130 
131     switch (s->curr_ccc) {
132     case I3C_CCCD_GETMXDS:
133         /* Default data rate for I3C. */
134         while (s->ccc_byte_offset < num_to_read) {
135             if (s->ccc_byte_offset >= 2) {
136                 break;
137             }
138             data[s->ccc_byte_offset] = 0;
139             *num_read = s->ccc_byte_offset;
140             s->ccc_byte_offset++;
141         }
142         break;
143     case I3C_CCCD_GETCAPS:
144         /* Support I3C version 1.1.x, no other features. */
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] = 0;
151             } else {
152                 data[s->ccc_byte_offset] = 0x01;
153             }
154             *num_read = s->ccc_byte_offset;
155             s->ccc_byte_offset++;
156         }
157         break;
158     case I3C_CCCD_GETMWL:
159     case I3C_CCCD_GETMRL:
160         /* MWL/MRL is MSB first. */
161         while (s->ccc_byte_offset < num_to_read) {
162             if (s->ccc_byte_offset >= 2) {
163                 break;
164             }
165             data[s->ccc_byte_offset] = (s->cfg.buf_size &
166                                         (0xff00 >> (s->ccc_byte_offset * 8))) >>
167                                         (8 - (s->ccc_byte_offset * 8));
168             s->ccc_byte_offset++;
169             *num_read = num_to_read;
170         }
171         break;
172     case I3C_CCC_ENTDAA:
173     case I3C_CCCD_GETPID:
174     case I3C_CCCD_GETBCR:
175     case I3C_CCCD_GETDCR:
176         /* Nothing to do. */
177         break;
178     default:
179         qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc);
180         return -1;
181     }
182 
183     trace_mock_target_handle_ccc_read(*num_read, num_to_read);
184     return 0;
185 }
186 
mock_target_handle_ccc_write(I3CTarget * i3c,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)187 static int mock_target_handle_ccc_write(I3CTarget *i3c, const uint8_t *data,
188                                         uint32_t num_to_send,
189                                         uint32_t *num_sent)
190 {
191     MockTargetState *s = MOCK_TARGET(i3c);
192 
193     if (!s->curr_ccc) {
194         s->in_ccc = true;
195         s->curr_ccc = *data;
196         trace_mock_target_new_ccc(s->curr_ccc);
197     }
198 
199     *num_sent = 1;
200     switch (s->curr_ccc) {
201     case I3C_CCC_ENEC:
202         s->can_ibi = true;
203         break;
204     case I3C_CCC_DISEC:
205         s->can_ibi = false;
206         break;
207     case I3C_CCC_ENTDAA:
208     case I3C_CCC_SETAASA:
209     case I3C_CCC_RSTDAA:
210     case I3C_CCCD_SETDASA:
211     case I3C_CCCD_GETPID:
212     case I3C_CCCD_GETBCR:
213     case I3C_CCCD_GETDCR:
214     case I3C_CCCD_GETMWL:
215     case I3C_CCCD_GETMRL:
216     case I3C_CCCD_GETMXDS:
217     case I3C_CCCD_GETCAPS:
218         /* Nothing to do. */
219         break;
220     default:
221         qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_ccc);
222         return -1;
223     }
224 
225     trace_mock_target_handle_ccc_read(*num_sent, num_to_send);
226     return 0;
227 }
228 
mock_target_do_ibi(MockTargetState * s)229 static void mock_target_do_ibi(MockTargetState *s)
230 {
231     if (!s->can_ibi) {
232         DB_PRINTF("IBIs disabled by controller");
233         return;
234     }
235 
236     trace_mock_target_do_ibi(s->i3c.address, true);
237     int nack = i3c_target_send_ibi(&s->i3c, s->i3c.address, /*is_recv=*/true);
238     /* Getting NACKed isn't necessarily an error, just print it out. */
239     if (nack) {
240         DB_PRINTF("NACKed from controller when sending target interrupt.\n");
241     }
242 }
243 
mock_target_timer_elapsed(void * opaque)244 static void mock_target_timer_elapsed(void *opaque)
245 {
246     MockTargetState *s = MOCK_TARGET(opaque);
247     timer_del(&s->qtimer);
248     mock_target_do_ibi(s);
249 }
250 
mock_target_reset(I3CTarget * i3c)251 static void mock_target_reset(I3CTarget *i3c)
252 {
253     MockTargetState *s = MOCK_TARGET(i3c);
254     s->can_ibi = false;
255 }
256 
mock_target_realize(DeviceState * dev,Error ** errp)257 static void mock_target_realize(DeviceState *dev, Error **errp)
258 {
259     MockTargetState *s = MOCK_TARGET(dev);
260     s->buf = g_new0(uint8_t, s->cfg.buf_size);
261     mock_target_reset(&s->i3c);
262 }
263 
mock_target_init(Object * obj)264 static void mock_target_init(Object *obj)
265 {
266     MockTargetState *s = MOCK_TARGET(obj);
267     s->can_ibi = false;
268 
269     /* For IBIs. */
270     timer_init_ns(&s->qtimer, QEMU_CLOCK_VIRTUAL, mock_target_timer_elapsed, s);
271 }
272 
273 static Property remote_i3c_props[] = {
274     /* The size of the internal buffer. */
275     DEFINE_PROP_UINT32("buf-size", MockTargetState, cfg.buf_size, 0x100),
276     /*
277      * If the mock target receives this number, it will issue an IBI after
278      * 1 second. Disabled if the IBI magic number is 0.
279      */
280     DEFINE_PROP_UINT8("ibi-magic-num", MockTargetState, cfg.ibi_magic, 0x00),
281     DEFINE_PROP_END_OF_LIST(),
282 };
283 
mock_target_class_init(ObjectClass * klass,void * data)284 static void mock_target_class_init(ObjectClass *klass, void *data)
285 {
286     DeviceClass *dc = DEVICE_CLASS(klass);
287     I3CTargetClass *k = I3C_TARGET_CLASS(klass);
288 
289     dc->realize = mock_target_realize;
290     k->event = mock_target_event;
291     k->recv = mock_target_rx;
292     k->send = mock_target_tx;
293     k->handle_ccc_read = mock_target_handle_ccc_read;
294     k->handle_ccc_write = mock_target_handle_ccc_write;
295 
296     device_class_set_props(dc, remote_i3c_props);
297 }
298 
299 static const TypeInfo mock_target_info = {
300     .name          = TYPE_MOCK_TARGET,
301     .parent        = TYPE_I3C_TARGET,
302     .instance_size = sizeof(MockTargetState),
303     .instance_init = mock_target_init,
304     .class_init    = mock_target_class_init,
305 };
306 
mock_target_register_types(void)307 static void mock_target_register_types(void)
308 {
309     type_register_static(&mock_target_info);
310 }
311 
312 type_init(mock_target_register_types)
313