| 1 | /* |
| 2 | * QEMU I3C bus interface. |
| 3 | * |
| 4 | * Copyright 2025 Google LLC |
| 5 | * |
| 6 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #ifndef QEMU_INCLUDE_HW_I3C_I3C_H_ |
| 10 | #define QEMU_INCLUDE_HW_I3C_I3C_H_ |
| 11 | |
| 12 | #include "hw/core/qdev.h" |
| 13 | #include "qom/object.h" |
| 14 | #include "hw/i2c/i2c.h" |
| 15 | |
| 16 | #define TYPE_I3C_TARGET "i3c-target" |
| 17 | OBJECT_DECLARE_TYPE(I3CTarget, I3CTargetClass, I3C_TARGET) |
| 18 | |
| 19 | typedef enum I3CEvent { |
| 20 | I3C_START_RECV, |
| 21 | I3C_START_SEND, |
| 22 | I3C_STOP, |
| 23 | I3C_NACK, |
| 24 | } I3CEvent; |
| 25 | |
| 26 | typedef enum I3CCCC { |
| 27 | /* Broadcast CCCs */ |
| 28 | I3C_CCC_ENEC = 0x00, |
| 29 | I3C_CCC_DISEC = 0x01, |
| 30 | I3C_CCC_ENTAS0 = 0x02, |
| 31 | I3C_CCC_ENTAS1 = 0x03, |
| 32 | I3C_CCC_ENTAS2 = 0x04, |
| 33 | I3C_CCC_ENTAS3 = 0x05, |
| 34 | I3C_CCC_RSTDAA = 0x06, |
| 35 | I3C_CCC_ENTDAA = 0x07, |
| 36 | I3C_CCC_DEFTGTS = 0x08, |
| 37 | I3C_CCC_SETMWL = 0x09, |
| 38 | I3C_CCC_SETMRL = 0x0a, |
| 39 | I3C_CCC_ENTTM = 0x0b, |
| 40 | I3C_CCC_SETBUSCON = 0x0c, |
| 41 | I3C_CCC_ENDXFER = 0x12, |
| 42 | I3C_CCC_ENTHDR0 = 0x20, |
| 43 | I3C_CCC_ENTHDR1 = 0x21, |
| 44 | I3C_CCC_ENTHDR2 = 0x22, |
| 45 | I3C_CCC_ENTHDR3 = 0x23, |
| 46 | I3C_CCC_ENTHDR4 = 0x24, |
| 47 | I3C_CCC_ENTHDR5 = 0x25, |
| 48 | I3C_CCC_ENTHDR6 = 0x26, |
| 49 | I3C_CCC_ENTHDR7 = 0x27, |
| 50 | I3C_CCC_SETXTIME = 0x28, |
| 51 | I3C_CCC_SETAASA = 0x29, |
| 52 | I3C_CCC_RSTACT = 0x2a, |
| 53 | I3C_CCC_DEFGRPA = 0x2b, |
| 54 | I3C_CCC_RSTGRPA = 0x2c, |
| 55 | I3C_CCC_MLANE = 0x2d, |
| 56 | /* Direct CCCs */ |
| 57 | I3C_CCCD_ENEC = 0x80, |
| 58 | I3C_CCCD_DISEC = 0x81, |
| 59 | I3C_CCCD_ENTAS0 = 0x82, |
| 60 | I3C_CCCD_ENTAS1 = 0x83, |
| 61 | I3C_CCCD_ENTAS2 = 0x84, |
| 62 | I3C_CCCD_ENTAS3 = 0x85, |
| 63 | I3C_CCCD_SETDASA = 0x87, |
| 64 | I3C_CCCD_SETNEWDA = 0x88, |
| 65 | I3C_CCCD_SETMWL = 0x89, |
| 66 | I3C_CCCD_SETMRL = 0x8a, |
| 67 | I3C_CCCD_GETMWL = 0x8b, |
| 68 | I3C_CCCD_GETMRL = 0x8c, |
| 69 | I3C_CCCD_GETPID = 0x8d, |
| 70 | I3C_CCCD_GETBCR = 0x8e, |
| 71 | I3C_CCCD_GETDCR = 0x8f, |
| 72 | I3C_CCCD_GETSTATUS = 0x90, |
| 73 | I3C_CCCD_GETACCCR = 0x91, |
| 74 | I3C_CCCD_ENDXFER = 0x92, |
| 75 | I3C_CCCD_SETBRGTGT = 0x93, |
| 76 | I3C_CCCD_GETMXDS = 0x94, |
| 77 | I3C_CCCD_GETCAPS = 0x95, |
| 78 | I3C_CCCD_SETROUTE = 0x96, |
| 79 | I3C_CCCD_SETXTIME = 0x98, |
| 80 | I3C_CCCD_GETXTIME = 0x99, |
| 81 | I3C_CCCD_RSTACT = 0x9a, |
| 82 | I3C_CCCD_SETGRPA = 0x9b, |
| 83 | I3C_CCCD_RSTGRPA = 0x9c, |
| 84 | I3C_CCCD_MLANE = 0x9d, |
| 85 | } I3CCCC; |
| 86 | |
| 87 | #define CCC_IS_DIRECT(_ccc) (_ccc & 0x80) |
| 88 | |
| 89 | #define I3C_BROADCAST 0x7e |
| 90 | #define I3C_HJ_ADDR 0x02 |
| 91 | #define I3C_ENTDAA_SIZE 8 |
| 92 | |
| 93 | struct I3CTargetClass { |
| 94 | DeviceClass parent_class; |
| 95 | |
| 96 | /* |
| 97 | * Controller to target. Returns 0 for success, non-zero for NAK or other |
| 98 | * error. |
| 99 | */ |
| 100 | int (*send)(I3CTarget *s, const uint8_t *data, uint32_t num_to_send, |
| 101 | uint32_t *num_sent); |
| 102 | /* |
| 103 | * Target to controller. I3C targets are able to terminate reads early, so |
| 104 | * this returns the number of bytes read from the target. |
| 105 | */ |
| 106 | uint32_t (*recv)(I3CTarget *s, uint8_t *data, uint32_t num_to_read); |
| 107 | /* Notify the target of a bus state change. */ |
| 108 | int (*event)(I3CTarget *s, enum I3CEvent event); |
| 109 | /* |
| 110 | * Handle a read CCC transmitted from a controller. |
| 111 | * CCCs are I3C commands that I3C targets support. |
| 112 | * The target can NACK the CCC if it does not support it. |
| 113 | */ |
| 114 | int (*handle_ccc_read)(I3CTarget *s, uint8_t *data, uint32_t num_to_read, |
| 115 | uint32_t *num_read); |
| 116 | /* |
| 117 | * Handle a write CCC transmitted from a controller. |
| 118 | * CCCs are I3C commands that I3C targets support. |
| 119 | * The target can NACK the CCC if it does not support it. |
| 120 | */ |
| 121 | int (*handle_ccc_write)(I3CTarget *s, const uint8_t *data, |
| 122 | uint32_t num_to_send, uint32_t *num_sent); |
| 123 | |
| 124 | /* |
| 125 | * Matches and adds the candidate if the address matches the candidate's |
| 126 | * address. |
| 127 | * Returns true if the address matched, or if this was a broadcast, and |
| 128 | * updates the device list. Otherwise returns false. |
| 129 | */ |
| 130 | bool (*target_match)(I3CTarget *candidate, uint8_t address, bool is_read, |
| 131 | bool broadcast, bool in_entdaa); |
| 132 | }; |
| 133 | |
| 134 | struct I3CTarget { |
| 135 | DeviceState parent_obj; |
| 136 | |
| 137 | uint8_t address; |
| 138 | uint8_t static_address; |
| 139 | uint8_t dcr; |
| 140 | uint8_t bcr; |
| 141 | /* |
| 142 | * Provisioned ID. Since core.c sends this LSB-first during ENTDAA |
| 143 | * via (pid >> (offset * 8)) & 0xff, targets must store it |
| 144 | * pre-reversed so that pid[47:40] goes on the wire first, as |
| 145 | * required by the I3C spec. |
| 146 | * e.g. for a device with pid 0xAABBCCDDEEFF, store 0xFFEEDDCCBBAA. |
| 147 | */ |
| 148 | uint64_t pid; |
| 149 | |
| 150 | /* CCC State tracking. */ |
| 151 | I3CCCC curr_ccc; |
| 152 | uint8_t ccc_byte_offset; |
| 153 | bool in_ccc; |
| 154 | bool in_test_mode; |
| 155 | }; |
| 156 | |
| 157 | struct I3CNode { |
| 158 | I3CTarget *target; |
| 159 | QLIST_ENTRY(I3CNode) next; |
| 160 | }; |
| 161 | |
| 162 | typedef struct I3CNode I3CNode; |
| 163 | |
| 164 | typedef QLIST_HEAD(I3CNodeList, I3CNode) I3CNodeList; |
| 165 | |
| 166 | #define TYPE_I3C_BUS "i3c-bus" |
| 167 | OBJECT_DECLARE_TYPE(I3CBus, I3CBusClass, I3C_BUS) |
| 168 | |
| 169 | struct I3CBus { |
| 170 | BusState parent_obj; |
| 171 | |
| 172 | /* Legacy I2C. */ |
| 173 | I2CBus *i2c_bus; |
| 174 | |
| 175 | I3CNodeList current_devs; |
| 176 | bool broadcast; |
| 177 | uint8_t ccc; |
| 178 | bool in_ccc; |
| 179 | bool in_entdaa; |
| 180 | uint8_t saved_address; |
| 181 | }; |
| 182 | |
| 183 | struct I3CBusClass { |
| 184 | BusClass parent_class; |
| 185 | |
| 186 | /* Handle an incoming IBI request from a target */ |
| 187 | int (*ibi_handle) (I3CBus *bus, uint8_t addr, bool is_recv); |
| 188 | /* Receive data from an IBI request */ |
| 189 | int (*ibi_recv) (I3CBus *bus, uint8_t data); |
| 190 | /* Do anything that needs to be done, since the IBI is finished. */ |
| 191 | int (*ibi_finish) (I3CBus *bus); |
| 192 | }; |
| 193 | |
| 194 | I3CBus *i3c_init_bus(DeviceState *parent, const char *name); |
| 195 | I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent, |
| 196 | const char *name); |
| 197 | void i3c_set_target_address(I3CTarget *dev, uint8_t address); |
| 198 | bool i3c_bus_busy(I3CBus *bus); |
| 199 | |
| 200 | /* |
| 201 | * Start a transfer on an I3C bus. |
| 202 | * If is_recv is known at compile-time (i.e. a device will always be sending or |
| 203 | * will always be receiving at a certain point), prefer to use i3c_start_recv or |
| 204 | * i3c_start_send instead. |
| 205 | * |
| 206 | * Returns 0 on success, non-zero on an error. |
| 207 | */ |
| 208 | int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); |
| 209 | |
| 210 | /* |
| 211 | * Start a receive transfer on an I3C bus. |
| 212 | * |
| 213 | * Returns 0 on success, non-zero on an error |
| 214 | */ |
| 215 | int i3c_start_recv(I3CBus *bus, uint8_t address); |
| 216 | |
| 217 | /* |
| 218 | * Start a send transfer on an I3C bus. |
| 219 | * |
| 220 | * Returns 0 on success, non-zero on an error |
| 221 | */ |
| 222 | int i3c_start_send(I3CBus *bus, uint8_t address); |
| 223 | |
| 224 | void i3c_end_transfer(I3CBus *bus); |
| 225 | void i3c_nack(I3CBus *bus); |
| 226 | int i3c_send_byte(I3CBus *bus, uint8_t data); |
| 227 | int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send, |
| 228 | uint32_t *num_sent); |
| 229 | /* |
| 230 | * I3C receives can only NACK on a CCC. The target should NACK a CCC it does not |
| 231 | * support. |
| 232 | */ |
| 233 | int i3c_recv_byte(I3CBus *bus, uint8_t *data); |
| 234 | int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read, |
| 235 | uint32_t *num_read); |
| 236 | bool i3c_scan_bus(I3CBus *bus, uint8_t address, enum I3CEvent event); |
| 237 | int i3c_do_entdaa(I3CBus *bus, uint8_t address, uint64_t *pid, uint8_t *bcr, |
| 238 | uint8_t *dcr); |
| 239 | int i3c_start_device_transfer(I3CTarget *dev, int send_length); |
| 240 | bool i3c_target_match_and_add(I3CBus *bus, I3CTarget *target, uint8_t address, |
| 241 | enum I3CEvent event); |
| 242 | int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv); |
| 243 | int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data); |
| 244 | int i3c_target_ibi_finish(I3CTarget *t, uint8_t data); |
| 245 | |
| 246 | /* |
| 247 | * Legacy I2C functions. |
| 248 | * |
| 249 | * These are wrapper for I2C functions that take in an I3C bus instead of an I2C |
| 250 | * bus. Internally they use the I2C bus (and devices attached to it) that's a |
| 251 | * part of the I3C bus |
| 252 | */ |
| 253 | void legacy_i2c_nack(I3CBus *bus); |
| 254 | uint8_t legacy_i2c_recv(I3CBus *bus); |
| 255 | int legacy_i2c_send(I3CBus *bus, uint8_t data); |
| 256 | int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); |
| 257 | int legacy_i2c_start_recv(I3CBus *bus, uint8_t address); |
| 258 | int legacy_i2c_start_send(I3CBus *bus, uint8_t address); |
| 259 | void legacy_i2c_end_transfer(I3CBus *bus); |
| 260 | I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name, |
| 261 | uint8_t addr); |
| 262 | |
| 263 | /** |
| 264 | * Create an I3C Target. |
| 265 | * |
| 266 | * The target returned from this function still needs to be realized. |
| 267 | */ |
| 268 | I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr, |
| 269 | uint8_t bcr, uint64_t pid); |
| 270 | |
| 271 | /** |
| 272 | * Create and realize an I3C target. |
| 273 | * |
| 274 | * Create the target, initialize it, put it on the specified I3C bus, and |
| 275 | * realize it. |
| 276 | */ |
| 277 | I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, |
| 278 | uint8_t addr, uint8_t dcr, uint8_t bcr, |
| 279 | uint64_t pid); |
| 280 | |
| 281 | /* Realize and drop the reference count on an I3C target. */ |
| 282 | bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **errp); |
| 283 | |
| 284 | #endif /* QEMU_INCLUDE_HW_I3C_I3C_H_ */ |