1c2339f05SJoe Komlodi /*
2c2339f05SJoe Komlodi * QEMU I3C bus interface.
3c2339f05SJoe Komlodi *
4c2339f05SJoe Komlodi * Copyright 2023 Google LLC
5c2339f05SJoe Komlodi *
6c2339f05SJoe Komlodi * This program is free software; you can redistribute it and/or modify it
7c2339f05SJoe Komlodi * under the terms of the GNU General Public License as published by the
8c2339f05SJoe Komlodi * Free Software Foundation; either version 2 of the License, or
9c2339f05SJoe Komlodi * (at your option) any later version.
10c2339f05SJoe Komlodi *
11c2339f05SJoe Komlodi * This program is distributed in the hope that it will be useful, but WITHOUT
12c2339f05SJoe Komlodi * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13c2339f05SJoe Komlodi * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14c2339f05SJoe Komlodi * for more details.
15c2339f05SJoe Komlodi */
16c2339f05SJoe Komlodi
17c2339f05SJoe Komlodi #include "qemu/osdep.h"
18c2339f05SJoe Komlodi #include "qemu/log.h"
19c2339f05SJoe Komlodi #include "qapi/error.h"
20c2339f05SJoe Komlodi #include "trace.h"
21c2339f05SJoe Komlodi #include "hw/i3c/i3c.h"
22*77b1e667SJoe Komlodi #include "hw/hotplug.h"
23c2339f05SJoe Komlodi #include "hw/qdev-properties.h"
24c2339f05SJoe Komlodi
25c2339f05SJoe Komlodi static Property i3c_props[] = {
26c2339f05SJoe Komlodi DEFINE_PROP_UINT8("static-address", struct I3CTarget, static_address, 0),
27c2339f05SJoe Komlodi DEFINE_PROP_UINT8("dcr", struct I3CTarget, dcr, 0),
28c2339f05SJoe Komlodi DEFINE_PROP_UINT8("bcr", struct I3CTarget, bcr, 0),
29c2339f05SJoe Komlodi DEFINE_PROP_UINT64("pid", struct I3CTarget, pid, 0),
30c2339f05SJoe Komlodi DEFINE_PROP_END_OF_LIST(),
31c2339f05SJoe Komlodi };
32c2339f05SJoe Komlodi
i3c_realize(BusState * bus,Error ** errp)33*77b1e667SJoe Komlodi static void i3c_realize(BusState *bus, Error **errp)
34*77b1e667SJoe Komlodi {
35*77b1e667SJoe Komlodi qbus_set_bus_hotplug_handler(bus);
36*77b1e667SJoe Komlodi }
37*77b1e667SJoe Komlodi
i3c_class_init(ObjectClass * klass,void * data)38*77b1e667SJoe Komlodi static void i3c_class_init(ObjectClass *klass, void *data)
39*77b1e667SJoe Komlodi {
40*77b1e667SJoe Komlodi BusClass *k = BUS_CLASS(klass);
41*77b1e667SJoe Komlodi k->realize = i3c_realize;
42*77b1e667SJoe Komlodi }
43*77b1e667SJoe Komlodi
44c2339f05SJoe Komlodi static const TypeInfo i3c_bus_info = {
45c2339f05SJoe Komlodi .name = TYPE_I3C_BUS,
46c2339f05SJoe Komlodi .parent = TYPE_BUS,
47c2339f05SJoe Komlodi .instance_size = sizeof(I3CBus),
48c2339f05SJoe Komlodi .class_size = sizeof(I3CBusClass),
49*77b1e667SJoe Komlodi .class_init = i3c_class_init,
50*77b1e667SJoe Komlodi .interfaces = (InterfaceInfo[]) {
51*77b1e667SJoe Komlodi { TYPE_HOTPLUG_HANDLER },
52*77b1e667SJoe Komlodi { }
53*77b1e667SJoe Komlodi }
54c2339f05SJoe Komlodi };
55c2339f05SJoe Komlodi
i3c_init_bus(DeviceState * parent,const char * name)56c2339f05SJoe Komlodi I3CBus *i3c_init_bus(DeviceState *parent, const char *name)
57c2339f05SJoe Komlodi {
58c2339f05SJoe Komlodi return i3c_init_bus_type(TYPE_I3C_BUS, parent, name);
59c2339f05SJoe Komlodi }
60c2339f05SJoe Komlodi
i3c_init_bus_type(const char * type,DeviceState * parent,const char * name)61c2339f05SJoe Komlodi I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent,
62c2339f05SJoe Komlodi const char *name)
63c2339f05SJoe Komlodi {
64c2339f05SJoe Komlodi I3CBus *bus;
65c2339f05SJoe Komlodi
66c2339f05SJoe Komlodi bus = I3C_BUS(qbus_new(type, parent, name));
67c2339f05SJoe Komlodi QLIST_INIT(&bus->current_devs);
68c2339f05SJoe Komlodi bus->broadcast = false;
69c2339f05SJoe Komlodi bus->in_entdaa = false;
70c2339f05SJoe Komlodi bus->in_ccc = false;
71c2339f05SJoe Komlodi
72c2339f05SJoe Komlodi /* I2C init. */
73c2339f05SJoe Komlodi g_autofree gchar *i2c_bus_name = g_strdup_printf("%s-legacy-i2c", name);
74c2339f05SJoe Komlodi bus->i2c_bus = i2c_init_bus(parent, i2c_bus_name);
75c2339f05SJoe Komlodi
76c2339f05SJoe Komlodi return bus;
77c2339f05SJoe Komlodi }
78c2339f05SJoe Komlodi
i3c_bus_busy(I3CBus * bus)79c2339f05SJoe Komlodi bool i3c_bus_busy(I3CBus *bus)
80c2339f05SJoe Komlodi {
81c2339f05SJoe Komlodi return !QLIST_EMPTY(&bus->current_devs);
82c2339f05SJoe Komlodi }
83c2339f05SJoe Komlodi
i3c_target_match(I3CBus * bus,I3CTarget * target,uint8_t address)84c2339f05SJoe Komlodi bool i3c_target_match(I3CBus *bus, I3CTarget *target, uint8_t address)
85c2339f05SJoe Komlodi {
86c2339f05SJoe Komlodi /* Once a target has a dynamic address, it only responds to that. */
87c2339f05SJoe Komlodi uint8_t targ_addr = target->address ? target->address :
88c2339f05SJoe Komlodi target->static_address;
89c2339f05SJoe Komlodi
90c2339f05SJoe Komlodi if (bus->in_entdaa) {
91c2339f05SJoe Komlodi if (address != I3C_BROADCAST) {
92c2339f05SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C Address 0x%.2x sent during "
93c2339f05SJoe Komlodi "ENTDAA instead of a broadcast address\n",
94c2339f05SJoe Komlodi object_get_canonical_path(OBJECT(bus)), address);
95c2339f05SJoe Komlodi return false;
96c2339f05SJoe Komlodi }
97c2339f05SJoe Komlodi
98c2339f05SJoe Komlodi /*
99c2339f05SJoe Komlodi * Targets should only ACK ENTDAA broadcasts if they have no dynamic
100c2339f05SJoe Komlodi * address.
101c2339f05SJoe Komlodi */
102c2339f05SJoe Komlodi if (target->address == 0) {
103c2339f05SJoe Komlodi I3CNode *node = g_new(struct I3CNode, 1);
104c2339f05SJoe Komlodi node->target = target;
105c2339f05SJoe Komlodi QLIST_INSERT_HEAD(&bus->current_devs, node, next);
106c2339f05SJoe Komlodi }
107c2339f05SJoe Komlodi return target->address == 0;
108c2339f05SJoe Komlodi }
109c2339f05SJoe Komlodi
110c2339f05SJoe Komlodi if ((targ_addr == address) || bus->broadcast) {
111c2339f05SJoe Komlodi I3CNode *node = g_new(struct I3CNode, 1);
112c2339f05SJoe Komlodi node->target = target;
113c2339f05SJoe Komlodi QLIST_INSERT_HEAD(&bus->current_devs, node, next);
114c2339f05SJoe Komlodi return true;
115c2339f05SJoe Komlodi }
116c2339f05SJoe Komlodi
117c2339f05SJoe Komlodi return false;
118c2339f05SJoe Komlodi }
119c2339f05SJoe Komlodi
i3c_scan_bus(I3CBus * bus,uint8_t address)120c2339f05SJoe Komlodi bool i3c_scan_bus(I3CBus *bus, uint8_t address)
121c2339f05SJoe Komlodi {
122c2339f05SJoe Komlodi BusChild *child;
123c2339f05SJoe Komlodi I3CNode *node, *next;
124c2339f05SJoe Komlodi
125c2339f05SJoe Komlodi /* Clear out any devices from a previous (re-)START. */
126c2339f05SJoe Komlodi QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
127c2339f05SJoe Komlodi QLIST_REMOVE(node, next);
128c2339f05SJoe Komlodi g_free(node);
129c2339f05SJoe Komlodi }
130c2339f05SJoe Komlodi
131c2339f05SJoe Komlodi QTAILQ_FOREACH(child, &bus->qbus.children, sibling) {
132c2339f05SJoe Komlodi DeviceState *qdev = child->child;
133c2339f05SJoe Komlodi I3CTarget *target = I3C_TARGET(qdev);
134c2339f05SJoe Komlodi
135c2339f05SJoe Komlodi if (i3c_target_match(bus, target, address)) {
136c2339f05SJoe Komlodi return true;
137c2339f05SJoe Komlodi }
138c2339f05SJoe Komlodi }
139c2339f05SJoe Komlodi
140c2339f05SJoe Komlodi /* No one on the bus could respond. */
141c2339f05SJoe Komlodi return false;
142c2339f05SJoe Komlodi }
143c2339f05SJoe Komlodi
144c2339f05SJoe Komlodi /* Class-level event handling, since we do some CCCs at the class level. */
i3c_target_event(I3CTarget * t,enum I3CEvent event)145c2339f05SJoe Komlodi static int i3c_target_event(I3CTarget *t, enum I3CEvent event)
146c2339f05SJoe Komlodi {
147c2339f05SJoe Komlodi I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t);
148c2339f05SJoe Komlodi trace_i3c_target_event(t->address, event);
149c2339f05SJoe Komlodi
150c2339f05SJoe Komlodi if (event == I3C_STOP) {
151c2339f05SJoe Komlodi t->curr_ccc = 0;
152c2339f05SJoe Komlodi t->ccc_byte_offset = 0;
153c2339f05SJoe Komlodi t->in_ccc = false;
154c2339f05SJoe Komlodi }
155c2339f05SJoe Komlodi return tc->event(t, event);
156c2339f05SJoe Komlodi }
157c2339f05SJoe Komlodi
158c2339f05SJoe Komlodi /*
159c2339f05SJoe Komlodi * Sends a START or repeated START and the address for an I3C transaction.
160c2339f05SJoe Komlodi *
161c2339f05SJoe Komlodi * This function returns 0 if a device on the bus was able to respond to the
162c2339f05SJoe Komlodi * address, and non-zero otherwise.
163c2339f05SJoe Komlodi * A non-zero return represents a NACK.
164c2339f05SJoe Komlodi */
i3c_do_start_transfer(I3CBus * bus,uint8_t address,enum I3CEvent event)165c2339f05SJoe Komlodi static int i3c_do_start_transfer(I3CBus *bus, uint8_t address,
166c2339f05SJoe Komlodi enum I3CEvent event)
167c2339f05SJoe Komlodi {
168c2339f05SJoe Komlodi I3CTargetClass *tc;
169c2339f05SJoe Komlodi I3CNode *node;
170c2339f05SJoe Komlodi
171c2339f05SJoe Komlodi if (address == I3C_BROADCAST) {
172c2339f05SJoe Komlodi bus->broadcast = true;
173c2339f05SJoe Komlodi /* If we're not in ENTDAA, a broadcast is the start of a new CCC. */
174c2339f05SJoe Komlodi if (!bus->in_entdaa) {
175c2339f05SJoe Komlodi bus->in_ccc = false;
176c2339f05SJoe Komlodi }
177c2339f05SJoe Komlodi } else {
178c2339f05SJoe Komlodi bus->broadcast = false;
179c2339f05SJoe Komlodi }
180c2339f05SJoe Komlodi
181c2339f05SJoe Komlodi /* No one responded to the address, NACK it. */
182c2339f05SJoe Komlodi if (!i3c_scan_bus(bus, address)) {
183c2339f05SJoe Komlodi return -1;
184c2339f05SJoe Komlodi }
185c2339f05SJoe Komlodi
186c2339f05SJoe Komlodi QLIST_FOREACH(node, &bus->current_devs, next) {
187c2339f05SJoe Komlodi I3CTarget *t = node->target;
188c2339f05SJoe Komlodi
189c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(t);
190c2339f05SJoe Komlodi if (tc->event) {
191c2339f05SJoe Komlodi int rv = i3c_target_event(t, event);
192c2339f05SJoe Komlodi if (rv && !bus->broadcast) {
193c2339f05SJoe Komlodi return rv;
194c2339f05SJoe Komlodi }
195c2339f05SJoe Komlodi }
196c2339f05SJoe Komlodi }
197c2339f05SJoe Komlodi
198c2339f05SJoe Komlodi return 0;
199c2339f05SJoe Komlodi }
200c2339f05SJoe Komlodi
i3c_start_transfer(I3CBus * bus,uint8_t address,bool is_recv)201c2339f05SJoe Komlodi int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv)
202c2339f05SJoe Komlodi {
203c2339f05SJoe Komlodi trace_i3c_start_transfer(address, is_recv);
204c2339f05SJoe Komlodi return i3c_do_start_transfer(bus, address, is_recv
205c2339f05SJoe Komlodi ? I3C_START_RECV
206c2339f05SJoe Komlodi : I3C_START_SEND);
207c2339f05SJoe Komlodi }
208c2339f05SJoe Komlodi
i3c_start_recv(I3CBus * bus,uint8_t address)209c2339f05SJoe Komlodi int i3c_start_recv(I3CBus *bus, uint8_t address)
210c2339f05SJoe Komlodi {
211c2339f05SJoe Komlodi trace_i3c_start_transfer(address, true);
212c2339f05SJoe Komlodi return i3c_do_start_transfer(bus, address, I3C_START_RECV);
213c2339f05SJoe Komlodi }
214c2339f05SJoe Komlodi
i3c_start_send(I3CBus * bus,uint8_t address)215c2339f05SJoe Komlodi int i3c_start_send(I3CBus *bus, uint8_t address)
216c2339f05SJoe Komlodi {
217c2339f05SJoe Komlodi trace_i3c_start_transfer(address, false);
218c2339f05SJoe Komlodi return i3c_do_start_transfer(bus, address, I3C_START_SEND);
219c2339f05SJoe Komlodi }
220c2339f05SJoe Komlodi
i3c_end_transfer(I3CBus * bus)221c2339f05SJoe Komlodi void i3c_end_transfer(I3CBus *bus)
222c2339f05SJoe Komlodi {
223c2339f05SJoe Komlodi I3CTargetClass *tc;
224c2339f05SJoe Komlodi I3CNode *node, *next;
225c2339f05SJoe Komlodi
226c2339f05SJoe Komlodi trace_i3c_end_transfer();
227c2339f05SJoe Komlodi
228c2339f05SJoe Komlodi /*
229c2339f05SJoe Komlodi * If we're in ENTDAA, we need to notify all devices when ENTDAA is done.
230c2339f05SJoe Komlodi * This is because everyone initially participates due to the broadcast,
231c2339f05SJoe Komlodi * but gradually drops out as they get assigned addresses.
232c2339f05SJoe Komlodi * Since the current_devs list only stores who's currently participating,
233c2339f05SJoe Komlodi * and not everyone who previously participated, we send the STOP to all
234c2339f05SJoe Komlodi * children.
235c2339f05SJoe Komlodi */
236c2339f05SJoe Komlodi if (bus->in_entdaa) {
237c2339f05SJoe Komlodi BusChild *child;
238c2339f05SJoe Komlodi
239c2339f05SJoe Komlodi QTAILQ_FOREACH(child, &bus->qbus.children, sibling) {
240c2339f05SJoe Komlodi DeviceState *qdev = child->child;
241c2339f05SJoe Komlodi I3CTarget *t = I3C_TARGET(qdev);
242c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(t);
243c2339f05SJoe Komlodi if (tc->event) {
244c2339f05SJoe Komlodi i3c_target_event(t, I3C_STOP);
245c2339f05SJoe Komlodi }
246c2339f05SJoe Komlodi }
247c2339f05SJoe Komlodi } else {
248c2339f05SJoe Komlodi QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
249c2339f05SJoe Komlodi I3CTarget *t = node->target;
250c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(t);
251c2339f05SJoe Komlodi if (tc->event) {
252c2339f05SJoe Komlodi i3c_target_event(t, I3C_STOP);
253c2339f05SJoe Komlodi }
254c2339f05SJoe Komlodi QLIST_REMOVE(node, next);
255c2339f05SJoe Komlodi g_free(node);
256c2339f05SJoe Komlodi }
257c2339f05SJoe Komlodi }
258c2339f05SJoe Komlodi bus->broadcast = false;
259c2339f05SJoe Komlodi bus->in_entdaa = false;
260c2339f05SJoe Komlodi bus->in_ccc = false;
261c2339f05SJoe Komlodi }
262c2339f05SJoe Komlodi
263c2339f05SJoe Komlodi /*
264c2339f05SJoe Komlodi * Any CCCs that are universal across all I3C devices should be handled here.
265c2339f05SJoe Komlodi * Once they're handled, we pass the CCC up to the I3C target to do anything
266c2339f05SJoe Komlodi * else it may want with the bytes.
267c2339f05SJoe Komlodi */
i3c_target_handle_ccc_write(I3CTarget * t,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)268c2339f05SJoe Komlodi static int i3c_target_handle_ccc_write(I3CTarget *t, const uint8_t *data,
269c2339f05SJoe Komlodi uint32_t num_to_send, uint32_t *num_sent)
270c2339f05SJoe Komlodi {
271c2339f05SJoe Komlodi I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t);
272c2339f05SJoe Komlodi *num_sent = 0;
273c2339f05SJoe Komlodi
274c2339f05SJoe Komlodi /* Is this the start of a new CCC? */
275c2339f05SJoe Komlodi if (!t->in_ccc) {
276c2339f05SJoe Komlodi t->curr_ccc = *data;
277c2339f05SJoe Komlodi t->in_ccc = true;
278c2339f05SJoe Komlodi *num_sent = 1;
279c2339f05SJoe Komlodi trace_i3c_target_handle_ccc(t->address, t->curr_ccc);
280c2339f05SJoe Komlodi }
281c2339f05SJoe Komlodi
282c2339f05SJoe Komlodi switch (t->curr_ccc) {
283c2339f05SJoe Komlodi case I3C_CCC_ENTDAA:
284c2339f05SJoe Komlodi /*
285c2339f05SJoe Komlodi * This is the last byte of ENTDAA, the controller is assigning us an
286c2339f05SJoe Komlodi * address.
287c2339f05SJoe Komlodi */
288c2339f05SJoe Komlodi if (t->ccc_byte_offset == 8) {
289c2339f05SJoe Komlodi t->address = *data;
290c2339f05SJoe Komlodi t->in_ccc = false;
291c2339f05SJoe Komlodi t->curr_ccc = 0;
292c2339f05SJoe Komlodi t->ccc_byte_offset = 0;
293c2339f05SJoe Komlodi *num_sent = 1;
294c2339f05SJoe Komlodi }
295c2339f05SJoe Komlodi break;
296c2339f05SJoe Komlodi case I3C_CCCD_SETDASA:
297c2339f05SJoe Komlodi t->address = t->static_address;
298c2339f05SJoe Komlodi break;
299c2339f05SJoe Komlodi case I3C_CCC_SETAASA:
300c2339f05SJoe Komlodi t->address = t->static_address;
301c2339f05SJoe Komlodi break;
302c2339f05SJoe Komlodi case I3C_CCC_RSTDAA:
303c2339f05SJoe Komlodi t->address = 0;
304c2339f05SJoe Komlodi break;
305c2339f05SJoe Komlodi case I3C_CCCD_SETNEWDA:
306c2339f05SJoe Komlodi /* If this isn't the CCC byte, it's our new address. */
307c2339f05SJoe Komlodi if (*num_sent == 0) {
308c2339f05SJoe Komlodi t->address = *data;
309c2339f05SJoe Komlodi *num_sent = 1;
310c2339f05SJoe Komlodi }
311c2339f05SJoe Komlodi break;
312c2339f05SJoe Komlodi /* Ignore other CCCs it's better to handle on a device-by-device basis. */
313c2339f05SJoe Komlodi default:
314c2339f05SJoe Komlodi break;
315c2339f05SJoe Komlodi }
316c2339f05SJoe Komlodi return tc->handle_ccc_write(t, data, num_to_send, num_sent);
317c2339f05SJoe Komlodi }
318c2339f05SJoe Komlodi
i3c_send_byte(I3CBus * bus,uint8_t data)319c2339f05SJoe Komlodi int i3c_send_byte(I3CBus *bus, uint8_t data)
320c2339f05SJoe Komlodi {
321c2339f05SJoe Komlodi /*
322c2339f05SJoe Komlodi * Ignored, the caller can determine how many were sent based on if this was
323c2339f05SJoe Komlodi * ACKed/NACKed.
324c2339f05SJoe Komlodi */
325c2339f05SJoe Komlodi uint32_t num_sent;
326c2339f05SJoe Komlodi return i3c_send(bus, &data, 1, &num_sent);
327c2339f05SJoe Komlodi }
328c2339f05SJoe Komlodi
i3c_send(I3CBus * bus,const uint8_t * data,uint32_t num_to_send,uint32_t * num_sent)329c2339f05SJoe Komlodi int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send,
330c2339f05SJoe Komlodi uint32_t *num_sent)
331c2339f05SJoe Komlodi {
332c2339f05SJoe Komlodi I3CTargetClass *tc;
333c2339f05SJoe Komlodi I3CTarget *t;
334c2339f05SJoe Komlodi I3CNode *node;
335c2339f05SJoe Komlodi int ret = 0;
336c2339f05SJoe Komlodi
337c2339f05SJoe Komlodi /* If this message is a broadcast and no CCC has been found, grab it. */
338c2339f05SJoe Komlodi if (bus->broadcast && !bus->in_ccc) {
339c2339f05SJoe Komlodi bus->ccc = *data;
340c2339f05SJoe Komlodi bus->in_ccc = true;
341c2339f05SJoe Komlodi /*
342c2339f05SJoe Komlodi * We need to keep track if we're currently in ENTDAA.
343c2339f05SJoe Komlodi * On any other CCC, the CCC is over on a RESTART or STOP, but ENTDAA
344c2339f05SJoe Komlodi * is only over on a STOP.
345c2339f05SJoe Komlodi */
346c2339f05SJoe Komlodi if (bus->ccc == I3C_CCC_ENTDAA) {
347c2339f05SJoe Komlodi bus->in_entdaa = true;
348c2339f05SJoe Komlodi }
349c2339f05SJoe Komlodi }
350c2339f05SJoe Komlodi
351c2339f05SJoe Komlodi QLIST_FOREACH(node, &bus->current_devs, next) {
352c2339f05SJoe Komlodi t = node->target;
353c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(t);
354c2339f05SJoe Komlodi if (bus->in_ccc) {
355c2339f05SJoe Komlodi if (!tc->handle_ccc_write) {
356c2339f05SJoe Komlodi ret = -1;
357c2339f05SJoe Komlodi continue;
358c2339f05SJoe Komlodi }
359c2339f05SJoe Komlodi ret = i3c_target_handle_ccc_write(t, data, num_to_send, num_sent);
360c2339f05SJoe Komlodi /* Targets should only NACK on a direct CCC. */
361c2339f05SJoe Komlodi if (ret && !CCC_IS_DIRECT(bus->ccc)) {
362c2339f05SJoe Komlodi ret = 0;
363c2339f05SJoe Komlodi }
364c2339f05SJoe Komlodi } else {
365c2339f05SJoe Komlodi if (tc->send) {
366c2339f05SJoe Komlodi ret = ret || tc->send(t, data, num_to_send, num_sent);
367c2339f05SJoe Komlodi } else {
368c2339f05SJoe Komlodi ret = -1;
369c2339f05SJoe Komlodi }
370c2339f05SJoe Komlodi }
371c2339f05SJoe Komlodi }
372c2339f05SJoe Komlodi
373c2339f05SJoe Komlodi trace_i3c_send(*num_sent, num_to_send, ret == 0);
374c2339f05SJoe Komlodi
375c2339f05SJoe Komlodi return ret ? -1 : 0;
376c2339f05SJoe Komlodi }
377c2339f05SJoe Komlodi
i3c_target_handle_ccc_read(I3CTarget * t,uint8_t * data,uint32_t num_to_read,uint32_t * num_read)378c2339f05SJoe Komlodi static int i3c_target_handle_ccc_read(I3CTarget *t, uint8_t *data,
379c2339f05SJoe Komlodi uint32_t num_to_read, uint32_t *num_read)
380c2339f05SJoe Komlodi {
381c2339f05SJoe Komlodi I3CTargetClass *tc = I3C_TARGET_GET_CLASS(t);
382c2339f05SJoe Komlodi uint8_t read_count = 0;
383c2339f05SJoe Komlodi
384c2339f05SJoe Komlodi switch (t->curr_ccc) {
385c2339f05SJoe Komlodi case I3C_CCC_ENTDAA:
386c2339f05SJoe Komlodi /* Return the 6-byte PID, followed by BCR then DCR. */
387c2339f05SJoe Komlodi while (t->ccc_byte_offset < 6) {
388c2339f05SJoe Komlodi if (read_count >= num_to_read) {
389c2339f05SJoe Komlodi break;
390c2339f05SJoe Komlodi }
391c2339f05SJoe Komlodi data[read_count] = (t->pid >> (t->ccc_byte_offset * 8)) & 0xff;
392c2339f05SJoe Komlodi t->ccc_byte_offset++;
393c2339f05SJoe Komlodi read_count++;
394c2339f05SJoe Komlodi }
395c2339f05SJoe Komlodi if (read_count < num_to_read) {
396c2339f05SJoe Komlodi data[read_count] = t->bcr;
397c2339f05SJoe Komlodi t->ccc_byte_offset++;
398c2339f05SJoe Komlodi read_count++;
399c2339f05SJoe Komlodi }
400c2339f05SJoe Komlodi if (read_count < num_to_read) {
401c2339f05SJoe Komlodi data[read_count] = t->dcr;
402c2339f05SJoe Komlodi t->ccc_byte_offset++;
403c2339f05SJoe Komlodi read_count++;
404c2339f05SJoe Komlodi }
405c2339f05SJoe Komlodi *num_read = read_count;
406c2339f05SJoe Komlodi break;
407c2339f05SJoe Komlodi case I3C_CCCD_GETPID:
408c2339f05SJoe Komlodi while (t->ccc_byte_offset < 6) {
409c2339f05SJoe Komlodi if (read_count >= num_to_read) {
410c2339f05SJoe Komlodi break;
411c2339f05SJoe Komlodi }
412c2339f05SJoe Komlodi data[read_count] = (t->pid >> (t->ccc_byte_offset * 8)) & 0xff;
413c2339f05SJoe Komlodi t->ccc_byte_offset++;
414c2339f05SJoe Komlodi read_count++;
415c2339f05SJoe Komlodi }
416c2339f05SJoe Komlodi *num_read = read_count;
417c2339f05SJoe Komlodi break;
418c2339f05SJoe Komlodi case I3C_CCCD_GETBCR:
419c2339f05SJoe Komlodi *data = t->bcr;
420c2339f05SJoe Komlodi *num_read = 1;
421c2339f05SJoe Komlodi break;
422c2339f05SJoe Komlodi case I3C_CCCD_GETDCR:
423c2339f05SJoe Komlodi *data = t->dcr;
424c2339f05SJoe Komlodi *num_read = 1;
425c2339f05SJoe Komlodi break;
426c2339f05SJoe Komlodi default:
427c2339f05SJoe Komlodi /* Unhandled on the I3CTarget class level. */
428c2339f05SJoe Komlodi break;
429c2339f05SJoe Komlodi }
430c2339f05SJoe Komlodi
431c2339f05SJoe Komlodi return tc->handle_ccc_read(t, data, num_to_read, num_read);
432c2339f05SJoe Komlodi }
433c2339f05SJoe Komlodi
i3c_recv_byte(I3CBus * bus,uint8_t * data)434c2339f05SJoe Komlodi int i3c_recv_byte(I3CBus *bus, uint8_t *data)
435c2339f05SJoe Komlodi {
436c2339f05SJoe Komlodi /*
437c2339f05SJoe Komlodi * Ignored, the caller can determine how many bytes were read based on if
438c2339f05SJoe Komlodi * this is ACKed/NACKed.
439c2339f05SJoe Komlodi */
440c2339f05SJoe Komlodi uint32_t num_read;
441c2339f05SJoe Komlodi return i3c_recv(bus, data, 1, &num_read);
442c2339f05SJoe Komlodi }
443c2339f05SJoe Komlodi
i3c_recv(I3CBus * bus,uint8_t * data,uint32_t num_to_read,uint32_t * num_read)444c2339f05SJoe Komlodi int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read,
445c2339f05SJoe Komlodi uint32_t *num_read)
446c2339f05SJoe Komlodi {
447c2339f05SJoe Komlodi int ret = 0;
448c2339f05SJoe Komlodi I3CTargetClass *tc;
449c2339f05SJoe Komlodi I3CTarget *t;
450c2339f05SJoe Komlodi
451c2339f05SJoe Komlodi *data = 0xff;
452c2339f05SJoe Komlodi if (!QLIST_EMPTY(&bus->current_devs)) {
453c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(QLIST_FIRST(&bus->current_devs)->target);
454c2339f05SJoe Komlodi t = QLIST_FIRST(&bus->current_devs)->target;
455c2339f05SJoe Komlodi if (bus->in_ccc) {
456c2339f05SJoe Komlodi if (!tc->handle_ccc_read) {
457c2339f05SJoe Komlodi return -1;
458c2339f05SJoe Komlodi }
459c2339f05SJoe Komlodi ret = i3c_target_handle_ccc_read(t, data, num_to_read, num_read);
460c2339f05SJoe Komlodi } else {
461c2339f05SJoe Komlodi if (tc->recv) {
462c2339f05SJoe Komlodi /*
463c2339f05SJoe Komlodi * Targets cannot NACK on a direct transfer, so the data
464c2339f05SJoe Komlodi * is returned directly.
465c2339f05SJoe Komlodi */
466c2339f05SJoe Komlodi *num_read = tc->recv(t, data, num_to_read);
467c2339f05SJoe Komlodi }
468c2339f05SJoe Komlodi }
469c2339f05SJoe Komlodi }
470c2339f05SJoe Komlodi
471c2339f05SJoe Komlodi trace_i3c_recv(*num_read, num_to_read, ret == 0);
472c2339f05SJoe Komlodi
473c2339f05SJoe Komlodi return ret;
474c2339f05SJoe Komlodi }
475c2339f05SJoe Komlodi
i3c_nack(I3CBus * bus)476c2339f05SJoe Komlodi void i3c_nack(I3CBus *bus)
477c2339f05SJoe Komlodi {
478c2339f05SJoe Komlodi I3CTargetClass *tc;
479c2339f05SJoe Komlodi I3CNode *node;
480c2339f05SJoe Komlodi
481c2339f05SJoe Komlodi if (QLIST_EMPTY(&bus->current_devs)) {
482c2339f05SJoe Komlodi return;
483c2339f05SJoe Komlodi }
484c2339f05SJoe Komlodi
485c2339f05SJoe Komlodi QLIST_FOREACH(node, &bus->current_devs, next) {
486c2339f05SJoe Komlodi tc = I3C_TARGET_GET_CLASS(node->target);
487c2339f05SJoe Komlodi if (tc->event) {
488c2339f05SJoe Komlodi i3c_target_event(node->target, I3C_NACK);
489c2339f05SJoe Komlodi }
490c2339f05SJoe Komlodi }
491c2339f05SJoe Komlodi }
492c2339f05SJoe Komlodi
i3c_target_send_ibi(I3CTarget * t,uint8_t addr,bool is_recv)493c2339f05SJoe Komlodi int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv)
494c2339f05SJoe Komlodi {
495c2339f05SJoe Komlodi I3CBus *bus = I3C_BUS(t->qdev.parent_bus);
496c2339f05SJoe Komlodi I3CBusClass *bc = I3C_BUS_GET_CLASS(bus);
497c2339f05SJoe Komlodi trace_i3c_target_send_ibi(addr, is_recv);
498c2339f05SJoe Komlodi return bc->ibi_handle(bus, t, addr, is_recv);
499c2339f05SJoe Komlodi }
500c2339f05SJoe Komlodi
i3c_target_send_ibi_bytes(I3CTarget * t,uint8_t data)501c2339f05SJoe Komlodi int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data)
502c2339f05SJoe Komlodi {
503c2339f05SJoe Komlodi I3CBus *bus = I3C_BUS(t->qdev.parent_bus);
504c2339f05SJoe Komlodi I3CBusClass *bc = I3C_BUS_GET_CLASS(bus);
505c2339f05SJoe Komlodi trace_i3c_target_send_ibi_bytes(data);
506c2339f05SJoe Komlodi return bc->ibi_recv(bus, data);
507c2339f05SJoe Komlodi }
508c2339f05SJoe Komlodi
i3c_target_ibi_finish(I3CTarget * t,uint8_t data)509c2339f05SJoe Komlodi int i3c_target_ibi_finish(I3CTarget *t, uint8_t data)
510c2339f05SJoe Komlodi {
511c2339f05SJoe Komlodi I3CBus *bus = I3C_BUS(t->qdev.parent_bus);
512c2339f05SJoe Komlodi I3CBusClass *bc = I3C_BUS_GET_CLASS(bus);
513c2339f05SJoe Komlodi trace_i3c_target_ibi_finish();
514c2339f05SJoe Komlodi return bc->ibi_finish(bus);
515c2339f05SJoe Komlodi }
516c2339f05SJoe Komlodi
i3c_addr_is_rsvd(uint8_t addr)517c2339f05SJoe Komlodi static bool i3c_addr_is_rsvd(uint8_t addr)
518c2339f05SJoe Komlodi {
519c2339f05SJoe Komlodi const bool is_rsvd[255] = {
520c2339f05SJoe Komlodi [0x00] = true,
521c2339f05SJoe Komlodi [0x01] = true,
522c2339f05SJoe Komlodi [0x02] = true,
523c2339f05SJoe Komlodi [0x3e] = true,
524c2339f05SJoe Komlodi [0x5e] = true,
525c2339f05SJoe Komlodi [0x6e] = true,
526c2339f05SJoe Komlodi [0x76] = true,
527c2339f05SJoe Komlodi [0x7a] = true,
528c2339f05SJoe Komlodi [0x7c] = true,
529c2339f05SJoe Komlodi [0x7e] = true,
530c2339f05SJoe Komlodi [0x7f] = true,
531c2339f05SJoe Komlodi };
532c2339f05SJoe Komlodi
533c2339f05SJoe Komlodi return is_rsvd[addr];
534c2339f05SJoe Komlodi }
535c2339f05SJoe Komlodi
i3c_target_new(const char * name,uint8_t addr,uint8_t dcr,uint8_t bcr,uint64_t pid)536c2339f05SJoe Komlodi I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr,
537c2339f05SJoe Komlodi uint8_t bcr, uint64_t pid)
538c2339f05SJoe Komlodi {
539c2339f05SJoe Komlodi DeviceState *dev;
540c2339f05SJoe Komlodi
541c2339f05SJoe Komlodi dev = qdev_new(name);
542c2339f05SJoe Komlodi qdev_prop_set_uint8(dev, "static-address", addr);
543c2339f05SJoe Komlodi qdev_prop_set_uint8(dev, "dcr", dcr);
544c2339f05SJoe Komlodi qdev_prop_set_uint8(dev, "bcr", bcr);
545c2339f05SJoe Komlodi qdev_prop_set_uint64(dev, "pid", pid);
546c2339f05SJoe Komlodi
547c2339f05SJoe Komlodi if (i3c_addr_is_rsvd(addr)) {
548c2339f05SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C target created with reserved "
549c2339f05SJoe Komlodi "address 0x%.2x\n",
550c2339f05SJoe Komlodi object_get_canonical_path(OBJECT(dev)), addr);
551c2339f05SJoe Komlodi }
552c2339f05SJoe Komlodi return I3C_TARGET(dev);
553c2339f05SJoe Komlodi }
554c2339f05SJoe Komlodi
i3c_target_realize_and_unref(I3CTarget * dev,I3CBus * bus,Error ** errp)555c2339f05SJoe Komlodi bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **errp)
556c2339f05SJoe Komlodi {
557c2339f05SJoe Komlodi return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp);
558c2339f05SJoe Komlodi }
559c2339f05SJoe Komlodi
i3c_target_create_simple(I3CBus * bus,const char * name,uint8_t addr,uint8_t dcr,uint8_t bcr,uint64_t pid)560c2339f05SJoe Komlodi I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, uint8_t addr,
561c2339f05SJoe Komlodi uint8_t dcr, uint8_t bcr, uint64_t pid)
562c2339f05SJoe Komlodi {
563c2339f05SJoe Komlodi I3CTarget *dev = i3c_target_new(name, addr, dcr, bcr, pid);
564c2339f05SJoe Komlodi dev->address = 0;
565c2339f05SJoe Komlodi i3c_target_realize_and_unref(dev, bus, &error_abort);
566c2339f05SJoe Komlodi
567c2339f05SJoe Komlodi return dev;
568c2339f05SJoe Komlodi }
569c2339f05SJoe Komlodi
570c2339f05SJoe Komlodi /* Legacy I2C functions. */
legacy_i2c_nack(I3CBus * bus)571c2339f05SJoe Komlodi void legacy_i2c_nack(I3CBus *bus)
572c2339f05SJoe Komlodi {
573c2339f05SJoe Komlodi trace_legacy_i2c_nack();
574c2339f05SJoe Komlodi i2c_nack(bus->i2c_bus);
575c2339f05SJoe Komlodi }
576c2339f05SJoe Komlodi
legacy_i2c_recv(I3CBus * bus)577c2339f05SJoe Komlodi uint8_t legacy_i2c_recv(I3CBus *bus)
578c2339f05SJoe Komlodi {
579c2339f05SJoe Komlodi uint8_t byte = i2c_recv(bus->i2c_bus);
580c2339f05SJoe Komlodi trace_legacy_i2c_recv(byte);
581c2339f05SJoe Komlodi return byte;
582c2339f05SJoe Komlodi }
583c2339f05SJoe Komlodi
legacy_i2c_send(I3CBus * bus,uint8_t data)584c2339f05SJoe Komlodi int legacy_i2c_send(I3CBus *bus, uint8_t data)
585c2339f05SJoe Komlodi {
586c2339f05SJoe Komlodi trace_legacy_i2c_send(data);
587c2339f05SJoe Komlodi return i2c_send(bus->i2c_bus, data);
588c2339f05SJoe Komlodi }
589c2339f05SJoe Komlodi
legacy_i2c_start_transfer(I3CBus * bus,uint8_t address,bool is_recv)590c2339f05SJoe Komlodi int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv)
591c2339f05SJoe Komlodi {
592c2339f05SJoe Komlodi trace_legacy_i2c_start_transfer(address, is_recv);
593c2339f05SJoe Komlodi return i2c_start_transfer(bus->i2c_bus, address, is_recv);
594c2339f05SJoe Komlodi }
595c2339f05SJoe Komlodi
legacy_i2c_start_recv(I3CBus * bus,uint8_t address)596c2339f05SJoe Komlodi int legacy_i2c_start_recv(I3CBus *bus, uint8_t address)
597c2339f05SJoe Komlodi {
598c2339f05SJoe Komlodi trace_legacy_i2c_start_transfer(address, true);
599c2339f05SJoe Komlodi return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=*/true);
600c2339f05SJoe Komlodi }
601c2339f05SJoe Komlodi
legacy_i2c_start_send(I3CBus * bus,uint8_t address)602c2339f05SJoe Komlodi int legacy_i2c_start_send(I3CBus *bus, uint8_t address)
603c2339f05SJoe Komlodi {
604c2339f05SJoe Komlodi trace_legacy_i2c_start_transfer(address, false);
605c2339f05SJoe Komlodi return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=*/false);
606c2339f05SJoe Komlodi }
607c2339f05SJoe Komlodi
legacy_i2c_end_transfer(I3CBus * bus)608c2339f05SJoe Komlodi void legacy_i2c_end_transfer(I3CBus *bus)
609c2339f05SJoe Komlodi {
610c2339f05SJoe Komlodi trace_legacy_i2c_end_transfer();
611c2339f05SJoe Komlodi i2c_end_transfer(bus->i2c_bus);
612c2339f05SJoe Komlodi }
613c2339f05SJoe Komlodi
legacy_i2c_device_create_simple(I3CBus * bus,const char * name,uint8_t addr)614c2339f05SJoe Komlodi I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name,
615c2339f05SJoe Komlodi uint8_t addr)
616c2339f05SJoe Komlodi {
617c2339f05SJoe Komlodi I2CSlave *dev = i2c_slave_new(name, addr);
618c2339f05SJoe Komlodi
619c2339f05SJoe Komlodi i2c_slave_realize_and_unref(dev, bus->i2c_bus, &error_abort);
620c2339f05SJoe Komlodi return dev;
621c2339f05SJoe Komlodi }
622c2339f05SJoe Komlodi
i3c_target_class_init(ObjectClass * klass,void * data)623c2339f05SJoe Komlodi static void i3c_target_class_init(ObjectClass *klass, void *data)
624c2339f05SJoe Komlodi {
625c2339f05SJoe Komlodi DeviceClass *k = DEVICE_CLASS(klass);
626c2339f05SJoe Komlodi set_bit(DEVICE_CATEGORY_MISC, k->categories);
627c2339f05SJoe Komlodi k->bus_type = TYPE_I3C_BUS;
628c2339f05SJoe Komlodi device_class_set_props(k, i3c_props);
629c2339f05SJoe Komlodi }
630c2339f05SJoe Komlodi
631c2339f05SJoe Komlodi static const TypeInfo i3c_target_type_info = {
632c2339f05SJoe Komlodi .name = TYPE_I3C_TARGET,
633c2339f05SJoe Komlodi .parent = TYPE_DEVICE,
634c2339f05SJoe Komlodi .instance_size = sizeof(I3CTarget),
635c2339f05SJoe Komlodi .abstract = true,
636c2339f05SJoe Komlodi .class_size = sizeof(I3CTargetClass),
637c2339f05SJoe Komlodi .class_init = i3c_target_class_init,
638c2339f05SJoe Komlodi };
639c2339f05SJoe Komlodi
i3c_register_types(void)640c2339f05SJoe Komlodi static void i3c_register_types(void)
641c2339f05SJoe Komlodi {
642c2339f05SJoe Komlodi type_register_static(&i3c_bus_info);
643c2339f05SJoe Komlodi type_register_static(&i3c_target_type_info);
644c2339f05SJoe Komlodi }
645c2339f05SJoe Komlodi
646c2339f05SJoe Komlodi type_init(i3c_register_types)
647