xref: /openbmc/qemu/hw/i3c/remote-i3c.c (revision 762f59c8)
1*762f59c8SJoe Komlodi /*
2*762f59c8SJoe Komlodi  * Remote I3C Device
3*762f59c8SJoe Komlodi  *
4*762f59c8SJoe Komlodi  * Copyright (c) 2023 Google LLC
5*762f59c8SJoe Komlodi  *
6*762f59c8SJoe Komlodi  * This program is free software; you can redistribute it and/or modify it
7*762f59c8SJoe Komlodi  * under the terms of the GNU General Public License as published by the
8*762f59c8SJoe Komlodi  * Free Software Foundation; either version 2 of the License, or
9*762f59c8SJoe Komlodi  * (at your option) any later version.
10*762f59c8SJoe Komlodi  *
11*762f59c8SJoe Komlodi  * This program is distributed in the hope that it will be useful, but WITHOUT
12*762f59c8SJoe Komlodi  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*762f59c8SJoe Komlodi  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14*762f59c8SJoe Komlodi  * for more details.
15*762f59c8SJoe Komlodi  */
16*762f59c8SJoe Komlodi 
17*762f59c8SJoe Komlodi #include "qemu/osdep.h"
18*762f59c8SJoe Komlodi #include "qemu/bswap.h"
19*762f59c8SJoe Komlodi #include "qemu/log.h"
20*762f59c8SJoe Komlodi #include "qemu/fifo8.h"
21*762f59c8SJoe Komlodi #include "chardev/char-fe.h"
22*762f59c8SJoe Komlodi #include "trace.h"
23*762f59c8SJoe Komlodi #include "hw/i3c/i3c.h"
24*762f59c8SJoe Komlodi #include "hw/i3c/remote-i3c.h"
25*762f59c8SJoe Komlodi #include "hw/qdev-properties-system.h"
26*762f59c8SJoe Komlodi 
27*762f59c8SJoe Komlodi typedef enum {
28*762f59c8SJoe Komlodi     IBI_RX_STATE_DONE = 0,
29*762f59c8SJoe Komlodi     IBI_RX_STATE_READ_ADDR = 1,
30*762f59c8SJoe Komlodi     IBI_RX_STATE_READ_RNW = 2,
31*762f59c8SJoe Komlodi     IBI_RX_STATE_READ_SIZE = 3,
32*762f59c8SJoe Komlodi     IBI_RX_STATE_READ_DATA = 4,
33*762f59c8SJoe Komlodi } IBIRXState;
34*762f59c8SJoe Komlodi 
35*762f59c8SJoe Komlodi typedef struct {
36*762f59c8SJoe Komlodi     uint8_t addr;
37*762f59c8SJoe Komlodi     bool is_recv;
38*762f59c8SJoe Komlodi     uint32_t num_bytes;
39*762f59c8SJoe Komlodi     uint8_t *data;
40*762f59c8SJoe Komlodi } IBIData;
41*762f59c8SJoe Komlodi 
42*762f59c8SJoe Komlodi typedef struct {
43*762f59c8SJoe Komlodi     I3CTarget parent_obj;
44*762f59c8SJoe Komlodi     CharBackend chr;
45*762f59c8SJoe Komlodi     /* For ease of debugging. */
46*762f59c8SJoe Komlodi 
47*762f59c8SJoe Komlodi     struct {
48*762f59c8SJoe Komlodi         char *name;
49*762f59c8SJoe Komlodi         uint32_t buf_size;
50*762f59c8SJoe Komlodi     } cfg;
51*762f59c8SJoe Komlodi 
52*762f59c8SJoe Komlodi     /* Intermediate buffer to store IBI data received over socket. */
53*762f59c8SJoe Komlodi     IBIData ibi_data;
54*762f59c8SJoe Komlodi     Fifo8 tx_fifo;
55*762f59c8SJoe Komlodi     Fifo8 rx_fifo;
56*762f59c8SJoe Komlodi     uint8_t current_cmd;
57*762f59c8SJoe Komlodi     IBIRXState ibi_rx_state;
58*762f59c8SJoe Komlodi     /*
59*762f59c8SJoe Komlodi      * To keep track of where we are in reading in data that's longer than
60*762f59c8SJoe Komlodi      * 1-byte.
61*762f59c8SJoe Komlodi      */
62*762f59c8SJoe Komlodi     uint32_t ibi_bytes_rxed;
63*762f59c8SJoe Komlodi } RemoteI3C;
64*762f59c8SJoe Komlodi 
remote_i3c_recv(I3CTarget * t,uint8_t * data,uint32_t num_to_read)65*762f59c8SJoe Komlodi static uint32_t remote_i3c_recv(I3CTarget *t, uint8_t *data,
66*762f59c8SJoe Komlodi                                 uint32_t num_to_read)
67*762f59c8SJoe Komlodi {
68*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(t);
69*762f59c8SJoe Komlodi     uint8_t type = REMOTE_I3C_RECV;
70*762f59c8SJoe Komlodi     uint32_t num_read;
71*762f59c8SJoe Komlodi 
72*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, &type, 1);
73*762f59c8SJoe Komlodi     uint32_t num_to_read_le = cpu_to_le32(num_to_read);
74*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, (uint8_t *)&num_to_read_le,
75*762f59c8SJoe Komlodi                           sizeof(num_to_read_le));
76*762f59c8SJoe Komlodi     /*
77*762f59c8SJoe Komlodi      * The response will first contain the size of the packet, as a LE uint32.
78*762f59c8SJoe Komlodi      */
79*762f59c8SJoe Komlodi     qemu_chr_fe_read_all(&i3c->chr, (uint8_t *)&num_read, sizeof(num_read));
80*762f59c8SJoe Komlodi 
81*762f59c8SJoe Komlodi     num_read = le32_to_cpu(num_read);
82*762f59c8SJoe Komlodi     qemu_chr_fe_read_all(&i3c->chr, data, num_read);
83*762f59c8SJoe Komlodi     trace_remote_i3c_recv(i3c->cfg.name, num_read, num_to_read);
84*762f59c8SJoe Komlodi     return num_read;
85*762f59c8SJoe Komlodi }
86*762f59c8SJoe Komlodi 
remote_i3c_tx_in_progress(RemoteI3C * i3c)87*762f59c8SJoe Komlodi static inline bool remote_i3c_tx_in_progress(RemoteI3C *i3c)
88*762f59c8SJoe Komlodi {
89*762f59c8SJoe Komlodi     return !fifo8_is_empty(&i3c->tx_fifo);
90*762f59c8SJoe Komlodi }
91*762f59c8SJoe Komlodi 
remote_i3c_chr_send_bytes(RemoteI3C * i3c)92*762f59c8SJoe Komlodi static int remote_i3c_chr_send_bytes(RemoteI3C *i3c)
93*762f59c8SJoe Komlodi {
94*762f59c8SJoe Komlodi     uint32_t i;
95*762f59c8SJoe Komlodi     uint32_t num_bytes = fifo8_num_used(&i3c->tx_fifo);
96*762f59c8SJoe Komlodi     g_autofree uint8_t *buf = g_new0(uint8_t, num_bytes);
97*762f59c8SJoe Komlodi 
98*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, &i3c->current_cmd,
99*762f59c8SJoe Komlodi                           sizeof(i3c->current_cmd));
100*762f59c8SJoe Komlodi 
101*762f59c8SJoe Komlodi     /* The FIFO data is stored in a ring buffer, move it into a linear one. */
102*762f59c8SJoe Komlodi     for (i = 0; i < num_bytes; i++) {
103*762f59c8SJoe Komlodi         buf[i] = fifo8_pop(&i3c->tx_fifo);
104*762f59c8SJoe Komlodi     }
105*762f59c8SJoe Komlodi 
106*762f59c8SJoe Komlodi     uint32_t num_bytes_le = cpu_to_le32(num_bytes);
107*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, (uint8_t *)&num_bytes_le, 4);
108*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, buf, num_bytes);
109*762f59c8SJoe Komlodi     trace_remote_i3c_send(i3c->cfg.name, num_bytes, i3c->current_cmd ==
110*762f59c8SJoe Komlodi                                                    REMOTE_I3C_HANDLE_CCC_WRITE);
111*762f59c8SJoe Komlodi 
112*762f59c8SJoe Komlodi     return 0;
113*762f59c8SJoe Komlodi }
114*762f59c8SJoe Komlodi 
remote_i3c_tx_fifo_push(RemoteI3C * i3c,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)115*762f59c8SJoe Komlodi static bool remote_i3c_tx_fifo_push(RemoteI3C *i3c, const uint8_t *data,
116*762f59c8SJoe Komlodi                                     uint32_t num_to_send, uint32_t *num_sent)
117*762f59c8SJoe Komlodi {
118*762f59c8SJoe Komlodi     uint32_t num_to_push = num_to_send;
119*762f59c8SJoe Komlodi     bool ack = true;
120*762f59c8SJoe Komlodi 
121*762f59c8SJoe Komlodi     /*
122*762f59c8SJoe Komlodi      * For performance reasons, we buffer data being sent from the controller to
123*762f59c8SJoe Komlodi      * us.
124*762f59c8SJoe Komlodi      * If this FIFO has data in it, we transmit it when we receive an I3C
125*762f59c8SJoe Komlodi      * STOP or START.
126*762f59c8SJoe Komlodi      */
127*762f59c8SJoe Komlodi     if (fifo8_num_free(&i3c->tx_fifo) < num_to_send) {
128*762f59c8SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: TX FIFO buffer full.\n",
129*762f59c8SJoe Komlodi                       object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name);
130*762f59c8SJoe Komlodi         num_to_push = fifo8_num_free(&i3c->tx_fifo);
131*762f59c8SJoe Komlodi         ack = false;
132*762f59c8SJoe Komlodi     }
133*762f59c8SJoe Komlodi 
134*762f59c8SJoe Komlodi     *num_sent = num_to_push;
135*762f59c8SJoe Komlodi     for (uint32_t i = 0; i < num_to_push; i++) {
136*762f59c8SJoe Komlodi         fifo8_push(&i3c->tx_fifo, data[i]);
137*762f59c8SJoe Komlodi     }
138*762f59c8SJoe Komlodi 
139*762f59c8SJoe Komlodi     return ack;
140*762f59c8SJoe Komlodi }
141*762f59c8SJoe Komlodi 
remote_i3c_send(I3CTarget * t,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)142*762f59c8SJoe Komlodi static int remote_i3c_send(I3CTarget *t, const uint8_t *data,
143*762f59c8SJoe Komlodi                            uint32_t num_to_send, uint32_t *num_sent)
144*762f59c8SJoe Komlodi {
145*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(t);
146*762f59c8SJoe Komlodi     i3c->current_cmd = REMOTE_I3C_SEND;
147*762f59c8SJoe Komlodi     if (!remote_i3c_tx_fifo_push(i3c, data, num_to_send, num_sent)) {
148*762f59c8SJoe Komlodi         return -1;
149*762f59c8SJoe Komlodi     }
150*762f59c8SJoe Komlodi 
151*762f59c8SJoe Komlodi     return 0;
152*762f59c8SJoe Komlodi }
153*762f59c8SJoe Komlodi 
remote_i3c_handle_ccc_read(I3CTarget * t,uint8_t * data,uint32_t num_to_read,uint32_t * num_read)154*762f59c8SJoe Komlodi static int remote_i3c_handle_ccc_read(I3CTarget *t, uint8_t *data,
155*762f59c8SJoe Komlodi                                       uint32_t num_to_read, uint32_t *num_read)
156*762f59c8SJoe Komlodi {
157*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(t);
158*762f59c8SJoe Komlodi     uint8_t type = REMOTE_I3C_HANDLE_CCC_READ;
159*762f59c8SJoe Komlodi 
160*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, &type, 1);
161*762f59c8SJoe Komlodi     /*
162*762f59c8SJoe Komlodi      * The response will first contain the size of the packet, as a LE uint32.
163*762f59c8SJoe Komlodi      */
164*762f59c8SJoe Komlodi     qemu_chr_fe_read_all(&i3c->chr, (uint8_t *)num_read, 4);
165*762f59c8SJoe Komlodi     *num_read = le32_to_cpu(*num_read);
166*762f59c8SJoe Komlodi     qemu_chr_fe_read_all(&i3c->chr, data, *num_read);
167*762f59c8SJoe Komlodi     trace_remote_i3c_ccc_read(i3c->cfg.name, *num_read, num_to_read);
168*762f59c8SJoe Komlodi 
169*762f59c8SJoe Komlodi     return 0;
170*762f59c8SJoe Komlodi }
171*762f59c8SJoe Komlodi 
remote_i3c_handle_ccc_write(I3CTarget * t,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)172*762f59c8SJoe Komlodi static int remote_i3c_handle_ccc_write(I3CTarget *t, const uint8_t *data,
173*762f59c8SJoe Komlodi                                        uint32_t num_to_send, uint32_t *num_sent)
174*762f59c8SJoe Komlodi {
175*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(t);
176*762f59c8SJoe Komlodi     i3c->current_cmd = REMOTE_I3C_HANDLE_CCC_WRITE;
177*762f59c8SJoe Komlodi     if (!remote_i3c_tx_fifo_push(i3c, data, num_to_send, num_sent)) {
178*762f59c8SJoe Komlodi         return -1;
179*762f59c8SJoe Komlodi     }
180*762f59c8SJoe Komlodi 
181*762f59c8SJoe Komlodi     return 0;
182*762f59c8SJoe Komlodi }
183*762f59c8SJoe Komlodi 
remote_i3c_event(I3CTarget * t,enum I3CEvent event)184*762f59c8SJoe Komlodi static int remote_i3c_event(I3CTarget *t, enum I3CEvent event)
185*762f59c8SJoe Komlodi {
186*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(t);
187*762f59c8SJoe Komlodi     uint8_t type;
188*762f59c8SJoe Komlodi     trace_remote_i3c_event(i3c->cfg.name, event);
189*762f59c8SJoe Komlodi     switch (event) {
190*762f59c8SJoe Komlodi     case I3C_START_RECV:
191*762f59c8SJoe Komlodi         type = REMOTE_I3C_START_RECV;
192*762f59c8SJoe Komlodi         break;
193*762f59c8SJoe Komlodi     case I3C_START_SEND:
194*762f59c8SJoe Komlodi         type = REMOTE_I3C_START_SEND;
195*762f59c8SJoe Komlodi         break;
196*762f59c8SJoe Komlodi     case I3C_STOP:
197*762f59c8SJoe Komlodi         type = REMOTE_I3C_STOP;
198*762f59c8SJoe Komlodi         break;
199*762f59c8SJoe Komlodi     case I3C_NACK:
200*762f59c8SJoe Komlodi         type = REMOTE_I3C_NACK;
201*762f59c8SJoe Komlodi         break;
202*762f59c8SJoe Komlodi     default:
203*762f59c8SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Unknown I3C event %d\n",
204*762f59c8SJoe Komlodi                       object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name,
205*762f59c8SJoe Komlodi                                                 event);
206*762f59c8SJoe Komlodi         return -1;
207*762f59c8SJoe Komlodi     }
208*762f59c8SJoe Komlodi 
209*762f59c8SJoe Komlodi     /*
210*762f59c8SJoe Komlodi      * If we have a transfer buffered, send it out before we tell the remote
211*762f59c8SJoe Komlodi      * target about the next event.
212*762f59c8SJoe Komlodi      */
213*762f59c8SJoe Komlodi     if (remote_i3c_tx_in_progress(i3c)) {
214*762f59c8SJoe Komlodi         remote_i3c_chr_send_bytes(i3c);
215*762f59c8SJoe Komlodi     }
216*762f59c8SJoe Komlodi 
217*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, &type, 1);
218*762f59c8SJoe Komlodi     return 0;
219*762f59c8SJoe Komlodi }
220*762f59c8SJoe Komlodi 
remote_i3c_chr_event(void * opaque,QEMUChrEvent evt)221*762f59c8SJoe Komlodi static void remote_i3c_chr_event(void *opaque, QEMUChrEvent evt)
222*762f59c8SJoe Komlodi {
223*762f59c8SJoe Komlodi     switch (evt) {
224*762f59c8SJoe Komlodi     case CHR_EVENT_OPENED:
225*762f59c8SJoe Komlodi     case CHR_EVENT_CLOSED:
226*762f59c8SJoe Komlodi     case CHR_EVENT_BREAK:
227*762f59c8SJoe Komlodi     case CHR_EVENT_MUX_IN:
228*762f59c8SJoe Komlodi     case CHR_EVENT_MUX_OUT:
229*762f59c8SJoe Komlodi         /*
230*762f59c8SJoe Komlodi          * Ignore events.
231*762f59c8SJoe Komlodi          * Our behavior stays the same regardless of what happens.
232*762f59c8SJoe Komlodi          */
233*762f59c8SJoe Komlodi         break;
234*762f59c8SJoe Komlodi     default:
235*762f59c8SJoe Komlodi         g_assert_not_reached();
236*762f59c8SJoe Komlodi     }
237*762f59c8SJoe Komlodi }
238*762f59c8SJoe Komlodi 
remote_i3c_rx_ibi(RemoteI3C * i3c,const uint8_t * buf,int size)239*762f59c8SJoe Komlodi static void remote_i3c_rx_ibi(RemoteI3C *i3c, const uint8_t *buf, int size)
240*762f59c8SJoe Komlodi {
241*762f59c8SJoe Komlodi     uint32_t p_buf = 0;
242*762f59c8SJoe Komlodi     while (p_buf < size) {
243*762f59c8SJoe Komlodi         switch (i3c->ibi_rx_state) {
244*762f59c8SJoe Komlodi         /* This is the start of a new IBI request. */
245*762f59c8SJoe Komlodi         case IBI_RX_STATE_DONE:
246*762f59c8SJoe Komlodi             i3c->ibi_rx_state = IBI_RX_STATE_READ_ADDR;
247*762f59c8SJoe Komlodi             p_buf++;
248*762f59c8SJoe Komlodi             break;
249*762f59c8SJoe Komlodi         case IBI_RX_STATE_READ_ADDR:
250*762f59c8SJoe Komlodi             i3c->ibi_data.addr = buf[p_buf];
251*762f59c8SJoe Komlodi             p_buf++;
252*762f59c8SJoe Komlodi             i3c->ibi_rx_state = IBI_RX_STATE_READ_RNW;
253*762f59c8SJoe Komlodi             break;
254*762f59c8SJoe Komlodi         case IBI_RX_STATE_READ_RNW:
255*762f59c8SJoe Komlodi             i3c->ibi_data.is_recv = buf[p_buf];
256*762f59c8SJoe Komlodi             p_buf++;
257*762f59c8SJoe Komlodi             i3c->ibi_rx_state = IBI_RX_STATE_READ_SIZE;
258*762f59c8SJoe Komlodi             break;
259*762f59c8SJoe Komlodi         case IBI_RX_STATE_READ_SIZE:
260*762f59c8SJoe Komlodi             i3c->ibi_data.num_bytes |= ((uint32_t)buf[p_buf] <<
261*762f59c8SJoe Komlodi                                         (8 * i3c->ibi_bytes_rxed));
262*762f59c8SJoe Komlodi             i3c->ibi_bytes_rxed++;
263*762f59c8SJoe Komlodi             p_buf++;
264*762f59c8SJoe Komlodi             /*
265*762f59c8SJoe Komlodi              * If we're done reading the num_bytes portion, move on to reading
266*762f59c8SJoe Komlodi              * data.
267*762f59c8SJoe Komlodi              */
268*762f59c8SJoe Komlodi             if (i3c->ibi_bytes_rxed == sizeof(i3c->ibi_data.num_bytes)) {
269*762f59c8SJoe Komlodi                 i3c->ibi_data.num_bytes = le32_to_cpu(i3c->ibi_data.num_bytes);
270*762f59c8SJoe Komlodi                 i3c->ibi_bytes_rxed = 0;
271*762f59c8SJoe Komlodi                 i3c->ibi_rx_state = IBI_RX_STATE_READ_DATA;
272*762f59c8SJoe Komlodi                 /* If there's no data to read, we're done. */
273*762f59c8SJoe Komlodi                 if (i3c->ibi_data.num_bytes == 0) {
274*762f59c8SJoe Komlodi                     /*
275*762f59c8SJoe Komlodi                      * Sanity check to see if the remote target is doing
276*762f59c8SJoe Komlodi                      * something wonky. This would only happen if it sends
277*762f59c8SJoe Komlodi                      * another IBI before the first one has been ACKed/NACKed
278*762f59c8SJoe Komlodi                      * by the controller.
279*762f59c8SJoe Komlodi                      * We'll try to recover by just exiting early and discarding
280*762f59c8SJoe Komlodi                      * the leftover bytes.
281*762f59c8SJoe Komlodi                      */
282*762f59c8SJoe Komlodi                     if (p_buf < size) {
283*762f59c8SJoe Komlodi                         qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target "
284*762f59c8SJoe Komlodi                                       "sent trailing bytes at the end of the "
285*762f59c8SJoe Komlodi                                       "IBI request.",
286*762f59c8SJoe Komlodi                             object_get_canonical_path(OBJECT(i3c)),
287*762f59c8SJoe Komlodi                                                       i3c->cfg.name);
288*762f59c8SJoe Komlodi                         return;
289*762f59c8SJoe Komlodi                     }
290*762f59c8SJoe Komlodi                     i3c->ibi_rx_state = IBI_RX_STATE_DONE;
291*762f59c8SJoe Komlodi                 } else {
292*762f59c8SJoe Komlodi                     /*
293*762f59c8SJoe Komlodi                      * We have IBI bytes to read, allocate memory for it.
294*762f59c8SJoe Komlodi                      * Freed when we're done sending the IBI to the controller.
295*762f59c8SJoe Komlodi                      */
296*762f59c8SJoe Komlodi                     i3c->ibi_data.data = g_new0(uint8_t,
297*762f59c8SJoe Komlodi                                                 i3c->ibi_data.num_bytes);
298*762f59c8SJoe Komlodi                 }
299*762f59c8SJoe Komlodi             }
300*762f59c8SJoe Komlodi             break;
301*762f59c8SJoe Komlodi         case IBI_RX_STATE_READ_DATA:
302*762f59c8SJoe Komlodi             i3c->ibi_data.data[i3c->ibi_bytes_rxed] = buf[p_buf];
303*762f59c8SJoe Komlodi             i3c->ibi_bytes_rxed++;
304*762f59c8SJoe Komlodi             p_buf++;
305*762f59c8SJoe Komlodi             if (i3c->ibi_bytes_rxed == i3c->ibi_data.num_bytes) {
306*762f59c8SJoe Komlodi                 /*
307*762f59c8SJoe Komlodi                  * Sanity check to see if the remote target is doing something
308*762f59c8SJoe Komlodi                  * wonky. This would only happen if it sends another IBI before
309*762f59c8SJoe Komlodi                  * the first one has been ACKed/NACKed by the controller.
310*762f59c8SJoe Komlodi                  * We'll try to recover by just exiting early and discarding the
311*762f59c8SJoe Komlodi                  * leftover bytes.
312*762f59c8SJoe Komlodi                  */
313*762f59c8SJoe Komlodi                 if (p_buf < size) {
314*762f59c8SJoe Komlodi                     qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target "
315*762f59c8SJoe Komlodi                                   "sent trailing bytes at the end of the "
316*762f59c8SJoe Komlodi                                   "IBI request.",
317*762f59c8SJoe Komlodi                         object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name);
318*762f59c8SJoe Komlodi                     return;
319*762f59c8SJoe Komlodi                 }
320*762f59c8SJoe Komlodi                 i3c->ibi_rx_state = IBI_RX_STATE_DONE;
321*762f59c8SJoe Komlodi             }
322*762f59c8SJoe Komlodi             break;
323*762f59c8SJoe Komlodi         default:
324*762f59c8SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Remote target IBI state "
325*762f59c8SJoe Komlodi                           "machine reached unknown state 0x%x\n",
326*762f59c8SJoe Komlodi                           object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name,
327*762f59c8SJoe Komlodi                           i3c->ibi_rx_state);
328*762f59c8SJoe Komlodi             g_assert_not_reached();
329*762f59c8SJoe Komlodi         }
330*762f59c8SJoe Komlodi     }
331*762f59c8SJoe Komlodi }
332*762f59c8SJoe Komlodi 
remote_i3c_ibi_rx_state_reset(RemoteI3C * i3c)333*762f59c8SJoe Komlodi static void remote_i3c_ibi_rx_state_reset(RemoteI3C *i3c)
334*762f59c8SJoe Komlodi {
335*762f59c8SJoe Komlodi     if (i3c->ibi_data.num_bytes) {
336*762f59c8SJoe Komlodi         free(i3c->ibi_data.data);
337*762f59c8SJoe Komlodi     }
338*762f59c8SJoe Komlodi     i3c->ibi_data.addr = 0;
339*762f59c8SJoe Komlodi     i3c->ibi_data.is_recv = 0;
340*762f59c8SJoe Komlodi     i3c->ibi_data.num_bytes = 0;
341*762f59c8SJoe Komlodi     i3c->ibi_bytes_rxed = 0;
342*762f59c8SJoe Komlodi     i3c->ibi_rx_state = IBI_RX_STATE_DONE;
343*762f59c8SJoe Komlodi }
344*762f59c8SJoe Komlodi 
remote_i3c_do_ibi(RemoteI3C * i3c)345*762f59c8SJoe Komlodi static void remote_i3c_do_ibi(RemoteI3C *i3c)
346*762f59c8SJoe Komlodi {
347*762f59c8SJoe Komlodi     uint32_t i;
348*762f59c8SJoe Komlodi     uint8_t resp = REMOTE_I3C_IBI_ACK;
349*762f59c8SJoe Komlodi 
350*762f59c8SJoe Komlodi     trace_remote_i3c_do_ibi(i3c->cfg.name, i3c->ibi_data.addr,
351*762f59c8SJoe Komlodi                             i3c->ibi_data.is_recv);
352*762f59c8SJoe Komlodi     if (i3c_target_send_ibi(&i3c->parent_obj, i3c->ibi_data.addr,
353*762f59c8SJoe Komlodi                         i3c->ibi_data.is_recv)) {
354*762f59c8SJoe Komlodi         resp = REMOTE_I3C_IBI_NACK;
355*762f59c8SJoe Komlodi     } else {
356*762f59c8SJoe Komlodi         for (i = 0; i < i3c->ibi_data.num_bytes; i++) {
357*762f59c8SJoe Komlodi             if (i3c_target_send_ibi_bytes(&i3c->parent_obj,
358*762f59c8SJoe Komlodi                                           i3c->ibi_data.data[i])) {
359*762f59c8SJoe Komlodi                 resp = REMOTE_I3C_IBI_DATA_NACK;
360*762f59c8SJoe Komlodi                 break;
361*762f59c8SJoe Komlodi             }
362*762f59c8SJoe Komlodi         }
363*762f59c8SJoe Komlodi     }
364*762f59c8SJoe Komlodi 
365*762f59c8SJoe Komlodi     if (i3c_target_ibi_finish(&i3c->parent_obj, 0x00)) {
366*762f59c8SJoe Komlodi         resp = REMOTE_I3C_IBI_NACK;
367*762f59c8SJoe Komlodi     }
368*762f59c8SJoe Komlodi     qemu_chr_fe_write_all(&i3c->chr, &resp, sizeof(resp));
369*762f59c8SJoe Komlodi     remote_i3c_ibi_rx_state_reset(i3c);
370*762f59c8SJoe Komlodi }
371*762f59c8SJoe Komlodi 
remote_i3c_chr_can_receive(void * opaque)372*762f59c8SJoe Komlodi static int remote_i3c_chr_can_receive(void *opaque)
373*762f59c8SJoe Komlodi {
374*762f59c8SJoe Komlodi     return 1;
375*762f59c8SJoe Komlodi }
376*762f59c8SJoe Komlodi 
remote_i3c_chr_receive(void * opaque,const uint8_t * buf,int size)377*762f59c8SJoe Komlodi static void remote_i3c_chr_receive(void *opaque, const uint8_t *buf, int size)
378*762f59c8SJoe Komlodi {
379*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(opaque);
380*762f59c8SJoe Komlodi 
381*762f59c8SJoe Komlodi     /*
382*762f59c8SJoe Komlodi      * The only things we expect to receive unprompted are:
383*762f59c8SJoe Komlodi      * - An ACK of a previous transfer
384*762f59c8SJoe Komlodi      * - A NACK of a previous transfer
385*762f59c8SJoe Komlodi      * - An IBI requested by the remote target.
386*762f59c8SJoe Komlodi      * - Bytes for an IBI request.
387*762f59c8SJoe Komlodi      */
388*762f59c8SJoe Komlodi     /* If we're in the middle of handling an IBI request, parse it here. */
389*762f59c8SJoe Komlodi     if (i3c->ibi_rx_state != IBI_RX_STATE_DONE) {
390*762f59c8SJoe Komlodi         remote_i3c_rx_ibi(i3c, buf, size);
391*762f59c8SJoe Komlodi         /* If we finished reading the IBI, do it. */
392*762f59c8SJoe Komlodi         if (i3c->ibi_rx_state == IBI_RX_STATE_DONE) {
393*762f59c8SJoe Komlodi             remote_i3c_do_ibi(i3c);
394*762f59c8SJoe Komlodi          }
395*762f59c8SJoe Komlodi          return;
396*762f59c8SJoe Komlodi     }
397*762f59c8SJoe Komlodi 
398*762f59c8SJoe Komlodi     switch (buf[0]) {
399*762f59c8SJoe Komlodi     case REMOTE_I3C_RX_ACK:
400*762f59c8SJoe Komlodi         break;
401*762f59c8SJoe Komlodi     case REMOTE_I3C_RX_NACK:
402*762f59c8SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Received NACK from remote "
403*762f59c8SJoe Komlodi                       "target\n", object_get_canonical_path(OBJECT(i3c)),
404*762f59c8SJoe Komlodi                       i3c->cfg.name);
405*762f59c8SJoe Komlodi         break;
406*762f59c8SJoe Komlodi     case REMOTE_I3C_IBI:
407*762f59c8SJoe Komlodi         remote_i3c_rx_ibi(i3c, buf, size);
408*762f59c8SJoe Komlodi         /* If we finished reading the IBI, do it. */
409*762f59c8SJoe Komlodi         if (i3c->ibi_rx_state == IBI_RX_STATE_DONE) {
410*762f59c8SJoe Komlodi             remote_i3c_do_ibi(i3c);
411*762f59c8SJoe Komlodi         }
412*762f59c8SJoe Komlodi         break;
413*762f59c8SJoe Komlodi     default:
414*762f59c8SJoe Komlodi         qemu_log_mask(LOG_GUEST_ERROR, "%s-%s: Unknown response 0x%x\n",
415*762f59c8SJoe Komlodi                       object_get_canonical_path(OBJECT(i3c)), i3c->cfg.name,
416*762f59c8SJoe Komlodi                       buf[0]);
417*762f59c8SJoe Komlodi         break;
418*762f59c8SJoe Komlodi     }
419*762f59c8SJoe Komlodi }
420*762f59c8SJoe Komlodi 
remote_i3c_realize(DeviceState * dev,Error ** errp)421*762f59c8SJoe Komlodi static void remote_i3c_realize(DeviceState *dev, Error **errp)
422*762f59c8SJoe Komlodi {
423*762f59c8SJoe Komlodi     RemoteI3C *i3c = REMOTE_I3C(dev);
424*762f59c8SJoe Komlodi 
425*762f59c8SJoe Komlodi     fifo8_create(&i3c->tx_fifo, i3c->cfg.buf_size);
426*762f59c8SJoe Komlodi     fifo8_create(&i3c->rx_fifo, i3c->cfg.buf_size);
427*762f59c8SJoe Komlodi     i3c->ibi_data.data = g_new0(uint8_t, i3c->cfg.buf_size);
428*762f59c8SJoe Komlodi     remote_i3c_ibi_rx_state_reset(i3c);
429*762f59c8SJoe Komlodi 
430*762f59c8SJoe Komlodi     qemu_chr_fe_set_handlers(&i3c->chr, remote_i3c_chr_can_receive,
431*762f59c8SJoe Komlodi                              remote_i3c_chr_receive, remote_i3c_chr_event,
432*762f59c8SJoe Komlodi                              NULL, OBJECT(i3c), NULL, true);
433*762f59c8SJoe Komlodi }
434*762f59c8SJoe Komlodi 
435*762f59c8SJoe Komlodi static Property remote_i3c_props[] = {
436*762f59c8SJoe Komlodi     DEFINE_PROP_CHR("chardev", RemoteI3C, chr),
437*762f59c8SJoe Komlodi     DEFINE_PROP_UINT32("buf-size", RemoteI3C, cfg.buf_size, 0x10000),
438*762f59c8SJoe Komlodi     DEFINE_PROP_STRING("device-name", RemoteI3C, cfg.name),
439*762f59c8SJoe Komlodi     DEFINE_PROP_END_OF_LIST(),
440*762f59c8SJoe Komlodi };
441*762f59c8SJoe Komlodi 
remote_i3c_class_init(ObjectClass * klass,void * data)442*762f59c8SJoe Komlodi static void remote_i3c_class_init(ObjectClass *klass, void *data)
443*762f59c8SJoe Komlodi {
444*762f59c8SJoe Komlodi     DeviceClass *dc = DEVICE_CLASS(klass);
445*762f59c8SJoe Komlodi     I3CTargetClass *k = I3C_TARGET_CLASS(klass);
446*762f59c8SJoe Komlodi 
447*762f59c8SJoe Komlodi     k->recv = &remote_i3c_recv;
448*762f59c8SJoe Komlodi     k->send = &remote_i3c_send;
449*762f59c8SJoe Komlodi     k->event = &remote_i3c_event;
450*762f59c8SJoe Komlodi     k->handle_ccc_read = &remote_i3c_handle_ccc_read;
451*762f59c8SJoe Komlodi     k->handle_ccc_write = &remote_i3c_handle_ccc_write;
452*762f59c8SJoe Komlodi     device_class_set_props(dc, remote_i3c_props);
453*762f59c8SJoe Komlodi     dc->realize = remote_i3c_realize;
454*762f59c8SJoe Komlodi }
455*762f59c8SJoe Komlodi 
456*762f59c8SJoe Komlodi static const TypeInfo remote_i3c_type = {
457*762f59c8SJoe Komlodi     .name = TYPE_REMOTE_I3C,
458*762f59c8SJoe Komlodi     .parent = TYPE_I3C_TARGET,
459*762f59c8SJoe Komlodi     .instance_size = sizeof(RemoteI3C),
460*762f59c8SJoe Komlodi     .class_size = sizeof(I3CTargetClass),
461*762f59c8SJoe Komlodi     .class_init = remote_i3c_class_init,
462*762f59c8SJoe Komlodi };
463*762f59c8SJoe Komlodi 
remote_i3c_register(void)464*762f59c8SJoe Komlodi static void remote_i3c_register(void)
465*762f59c8SJoe Komlodi {
466*762f59c8SJoe Komlodi     type_register_static(&remote_i3c_type);
467*762f59c8SJoe Komlodi }
468*762f59c8SJoe Komlodi 
469*762f59c8SJoe Komlodi type_init(remote_i3c_register)
470