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