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