17d87775fSJoe Komlodi /* 27d87775fSJoe Komlodi * ASPEED I3C Controller 37d87775fSJoe Komlodi * 47d87775fSJoe Komlodi * Copyright (C) 2021 ASPEED Technology Inc. 5e7b1406cSJoe Komlodi * Copyright (C) 2023 Google LLC 67d87775fSJoe Komlodi * 77d87775fSJoe Komlodi * This code is licensed under the GPL version 2 or later. See 87d87775fSJoe Komlodi * the COPYING file in the top-level directory. 97d87775fSJoe Komlodi */ 107d87775fSJoe Komlodi 117d87775fSJoe Komlodi #include "qemu/osdep.h" 127d87775fSJoe Komlodi #include "qemu/log.h" 137d87775fSJoe Komlodi #include "qemu/error-report.h" 147d87775fSJoe Komlodi #include "hw/i3c/aspeed_i3c.h" 157d87775fSJoe Komlodi #include "hw/registerfields.h" 167d87775fSJoe Komlodi #include "hw/qdev-properties.h" 177d87775fSJoe Komlodi #include "qapi/error.h" 187d87775fSJoe Komlodi #include "migration/vmstate.h" 197d87775fSJoe Komlodi #include "trace.h" 203816bedaSJoe Komlodi #include "hw/i3c/i3c.h" 213816bedaSJoe Komlodi #include "hw/irq.h" 227d87775fSJoe Komlodi 2393ec6949SJoe Komlodi /* 2493ec6949SJoe Komlodi * Disable event command values. sent along with a DISEC CCC to disable certain 2593ec6949SJoe Komlodi * events on targets. 2693ec6949SJoe Komlodi */ 2793ec6949SJoe Komlodi #define DISEC_HJ 0x08 2893ec6949SJoe Komlodi #define DISEC_CR 0x02 2993ec6949SJoe Komlodi #define DISEC_INT 0x01 3093ec6949SJoe Komlodi 317d87775fSJoe Komlodi /* I3C Controller Registers */ 327d87775fSJoe Komlodi REG32(I3C1_REG0, 0x10) 337d87775fSJoe Komlodi REG32(I3C1_REG1, 0x14) 347d87775fSJoe Komlodi FIELD(I3C1_REG1, I2C_MODE, 0, 1) 35e7b1406cSJoe Komlodi FIELD(I3C1_REG1, SLV_TEST_MODE, 1, 1) 36e7b1406cSJoe Komlodi FIELD(I3C1_REG1, ACT_MODE, 2, 2) 37e7b1406cSJoe Komlodi FIELD(I3C1_REG1, PENDING_INT, 4, 4) 38e7b1406cSJoe Komlodi FIELD(I3C1_REG1, SA, 8, 7) 397d87775fSJoe Komlodi FIELD(I3C1_REG1, SA_EN, 15, 1) 40e7b1406cSJoe Komlodi FIELD(I3C1_REG1, INST_ID, 16, 4) 417d87775fSJoe Komlodi REG32(I3C2_REG0, 0x20) 427d87775fSJoe Komlodi REG32(I3C2_REG1, 0x24) 437d87775fSJoe Komlodi FIELD(I3C2_REG1, I2C_MODE, 0, 1) 44e7b1406cSJoe Komlodi FIELD(I3C2_REG1, SLV_TEST_MODE, 1, 1) 45e7b1406cSJoe Komlodi FIELD(I3C2_REG1, ACT_MODE, 2, 2) 46e7b1406cSJoe Komlodi FIELD(I3C2_REG1, PENDING_INT, 4, 4) 47e7b1406cSJoe Komlodi FIELD(I3C2_REG1, SA, 8, 7) 487d87775fSJoe Komlodi FIELD(I3C2_REG1, SA_EN, 15, 1) 49e7b1406cSJoe Komlodi FIELD(I3C2_REG1, INST_ID, 16, 4) 507d87775fSJoe Komlodi REG32(I3C3_REG0, 0x30) 517d87775fSJoe Komlodi REG32(I3C3_REG1, 0x34) 527d87775fSJoe Komlodi FIELD(I3C3_REG1, I2C_MODE, 0, 1) 53e7b1406cSJoe Komlodi FIELD(I3C3_REG1, SLV_TEST_MODE, 1, 1) 54e7b1406cSJoe Komlodi FIELD(I3C3_REG1, ACT_MODE, 2, 2) 55e7b1406cSJoe Komlodi FIELD(I3C3_REG1, PENDING_INT, 4, 4) 56e7b1406cSJoe Komlodi FIELD(I3C3_REG1, SA, 8, 7) 577d87775fSJoe Komlodi FIELD(I3C3_REG1, SA_EN, 15, 1) 58e7b1406cSJoe Komlodi FIELD(I3C3_REG1, INST_ID, 16, 4) 597d87775fSJoe Komlodi REG32(I3C4_REG0, 0x40) 607d87775fSJoe Komlodi REG32(I3C4_REG1, 0x44) 617d87775fSJoe Komlodi FIELD(I3C4_REG1, I2C_MODE, 0, 1) 62e7b1406cSJoe Komlodi FIELD(I3C4_REG1, SLV_TEST_MODE, 1, 1) 63e7b1406cSJoe Komlodi FIELD(I3C4_REG1, ACT_MODE, 2, 2) 64e7b1406cSJoe Komlodi FIELD(I3C4_REG1, PENDING_INT, 4, 4) 65e7b1406cSJoe Komlodi FIELD(I3C4_REG1, SA, 8, 7) 667d87775fSJoe Komlodi FIELD(I3C4_REG1, SA_EN, 15, 1) 67e7b1406cSJoe Komlodi FIELD(I3C4_REG1, INST_ID, 16, 4) 687d87775fSJoe Komlodi REG32(I3C5_REG0, 0x50) 697d87775fSJoe Komlodi REG32(I3C5_REG1, 0x54) 707d87775fSJoe Komlodi FIELD(I3C5_REG1, I2C_MODE, 0, 1) 71e7b1406cSJoe Komlodi FIELD(I3C5_REG1, SLV_TEST_MODE, 1, 1) 72e7b1406cSJoe Komlodi FIELD(I3C5_REG1, ACT_MODE, 2, 2) 73e7b1406cSJoe Komlodi FIELD(I3C5_REG1, PENDING_INT, 4, 4) 74e7b1406cSJoe Komlodi FIELD(I3C5_REG1, SA, 8, 7) 757d87775fSJoe Komlodi FIELD(I3C5_REG1, SA_EN, 15, 1) 76e7b1406cSJoe Komlodi FIELD(I3C5_REG1, INST_ID, 16, 4) 777d87775fSJoe Komlodi REG32(I3C6_REG0, 0x60) 787d87775fSJoe Komlodi REG32(I3C6_REG1, 0x64) 797d87775fSJoe Komlodi FIELD(I3C6_REG1, I2C_MODE, 0, 1) 80e7b1406cSJoe Komlodi FIELD(I3C6_REG1, SLV_TEST_MODE, 1, 1) 81e7b1406cSJoe Komlodi FIELD(I3C6_REG1, ACT_MODE, 2, 2) 82e7b1406cSJoe Komlodi FIELD(I3C6_REG1, PENDING_INT, 4, 4) 83e7b1406cSJoe Komlodi FIELD(I3C6_REG1, SA, 8, 7) 847d87775fSJoe Komlodi FIELD(I3C6_REG1, SA_EN, 15, 1) 85e7b1406cSJoe Komlodi FIELD(I3C6_REG1, INST_ID, 16, 4) 867d87775fSJoe Komlodi 877d87775fSJoe Komlodi /* I3C Device Registers */ 887d87775fSJoe Komlodi REG32(DEVICE_CTRL, 0x00) 89e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, I3C_BROADCAST_ADDR_INC, 0, 1) 90e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, I2C_SLAVE_PRESENT, 7, 1) 91e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, HOT_JOIN_ACK_NACK_CTRL, 8, 1) 92e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, IDLE_CNT_MULTIPLIER, 24, 2) 93e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, SLV_ADAPT_TO_I2C_I3C_MODE, 27, 1) 94e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, DMA_HANDSHAKE_EN, 28, 1) 95e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, I3C_ABORT, 29, 1) 96e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, I3C_RESUME, 30, 1) 97e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL, I3C_EN, 31, 1) 987d87775fSJoe Komlodi REG32(DEVICE_ADDR, 0x04) 99e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR, STATIC_ADDR, 0, 7) 100e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR, STATIC_ADDR_VALID, 15, 1) 101e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR, DYNAMIC_ADDR, 16, 7) 102e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR, DYNAMIC_ADDR_VALID, 15, 1) 1037d87775fSJoe Komlodi REG32(HW_CAPABILITY, 0x08) 104e7b1406cSJoe Komlodi FIELD(HW_CAPABILITY, ENTDAA, 0, 1) 105e7b1406cSJoe Komlodi FIELD(HW_CAPABILITY, HDR_DDR, 3, 1) 106e7b1406cSJoe Komlodi FIELD(HW_CAPABILITY, HDR_TS, 4, 1) 1077d87775fSJoe Komlodi REG32(COMMAND_QUEUE_PORT, 0x0c) 108e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, CMD_ATTR, 0, 3) 109e7b1406cSJoe Komlodi /* Transfer command structure */ 110e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, TID, 3, 4) 111e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, CMD, 7, 8) 112e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, CP, 15, 1) 113e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, DEV_INDEX, 16, 5) 114e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, SPEED, 21, 3) 115e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, ROC, 26, 1) 116e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, SDAP, 27, 1) 117e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, RNW, 28, 1) 118e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, TOC, 30, 1) 119e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, PEC, 31, 1) 120e7b1406cSJoe Komlodi /* Transfer argument data structure */ 121e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, DB, 8, 8) 122e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, DL, 16, 16) 123e7b1406cSJoe Komlodi /* Short data argument data structure */ 124e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, BYTE_STRB, 3, 3) 125e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, BYTE0, 8, 8) 126e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, BYTE1, 16, 8) 127e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, BYTE2, 24, 8) 128e7b1406cSJoe Komlodi /* Address assignment command structure */ 129e7b1406cSJoe Komlodi /* 130e7b1406cSJoe Komlodi * bits 3..21 and 26..31 are the same as the transfer command structure, or 131e7b1406cSJoe Komlodi * marked as reserved. 132e7b1406cSJoe Komlodi */ 133e7b1406cSJoe Komlodi FIELD(COMMAND_QUEUE_PORT, DEV_COUNT, 21, 3) 1347d87775fSJoe Komlodi REG32(RESPONSE_QUEUE_PORT, 0x10) 135e7b1406cSJoe Komlodi FIELD(RESPONSE_QUEUE_PORT, DL, 0, 16) 136e7b1406cSJoe Komlodi FIELD(RESPONSE_QUEUE_PORT, CCCT, 16, 8) 137e7b1406cSJoe Komlodi FIELD(RESPONSE_QUEUE_PORT, TID, 24, 4) 138e7b1406cSJoe Komlodi FIELD(RESPONSE_QUEUE_PORT, ERR_STATUS, 28, 4) 1397d87775fSJoe Komlodi REG32(RX_TX_DATA_PORT, 0x14) 1407d87775fSJoe Komlodi REG32(IBI_QUEUE_STATUS, 0x18) 141e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_STATUS, IBI_DATA_LEN, 0, 8) 142e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_STATUS, IBI_ID, 8, 8) 143e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_STATUS, LAST_STATUS, 24, 1) 144e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_STATUS, ERROR, 30, 1) 145e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_STATUS, IBI_STATUS, 31, 1) 1467d87775fSJoe Komlodi REG32(IBI_QUEUE_DATA, 0x18) 1477d87775fSJoe Komlodi REG32(QUEUE_THLD_CTRL, 0x1c) 148e7b1406cSJoe Komlodi FIELD(QUEUE_THLD_CTRL, CMD_BUF_EMPTY_THLD, 0, 8); 149e7b1406cSJoe Komlodi FIELD(QUEUE_THLD_CTRL, RESP_BUF_THLD, 8, 8); 150e7b1406cSJoe Komlodi FIELD(QUEUE_THLD_CTRL, IBI_DATA_THLD, 16, 8); 151e7b1406cSJoe Komlodi FIELD(QUEUE_THLD_CTRL, IBI_STATUS_THLD, 24, 8); 1527d87775fSJoe Komlodi REG32(DATA_BUFFER_THLD_CTRL, 0x20) 153e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_THLD_CTRL, TX_BUF_THLD, 0, 3) 154e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_THLD_CTRL, RX_BUF_THLD, 10, 3) 155e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_THLD_CTRL, TX_START_THLD, 16, 3) 156e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_THLD_CTRL, RX_START_THLD, 24, 3) 1577d87775fSJoe Komlodi REG32(IBI_QUEUE_CTRL, 0x24) 158e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN, 0, 1) 159e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ, 1, 1) 160e7b1406cSJoe Komlodi FIELD(IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ, 3, 1) 1617d87775fSJoe Komlodi REG32(IBI_MR_REQ_REJECT, 0x2c) 1627d87775fSJoe Komlodi REG32(IBI_SIR_REQ_REJECT, 0x30) 1637d87775fSJoe Komlodi REG32(RESET_CTRL, 0x34) 164e7b1406cSJoe Komlodi FIELD(RESET_CTRL, CORE_RESET, 0, 1) 165e7b1406cSJoe Komlodi FIELD(RESET_CTRL, CMD_QUEUE_RESET, 1, 1) 166e7b1406cSJoe Komlodi FIELD(RESET_CTRL, RESP_QUEUE_RESET, 2, 1) 167e7b1406cSJoe Komlodi FIELD(RESET_CTRL, TX_BUF_RESET, 3, 1) 168e7b1406cSJoe Komlodi FIELD(RESET_CTRL, RX_BUF_RESET, 4, 1) 169e7b1406cSJoe Komlodi FIELD(RESET_CTRL, IBI_QUEUE_RESET, 5, 1) 1707d87775fSJoe Komlodi REG32(SLV_EVENT_CTRL, 0x38) 171e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, SLV_INTERRUPT, 0, 1) 172e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, MASTER_INTERRUPT, 1, 1) 173e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, HOT_JOIN_INTERRUPT, 3, 1) 174e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, ACTIVITY_STATE, 4, 2) 175e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, MRL_UPDATED, 6, 1) 176e7b1406cSJoe Komlodi FIELD(SLV_EVENT_CTRL, MWL_UPDATED, 7, 1) 1777d87775fSJoe Komlodi REG32(INTR_STATUS, 0x3c) 178e7b1406cSJoe Komlodi FIELD(INTR_STATUS, TX_THLD, 0, 1) 179e7b1406cSJoe Komlodi FIELD(INTR_STATUS, RX_THLD, 1, 1) 180e7b1406cSJoe Komlodi FIELD(INTR_STATUS, IBI_THLD, 2, 1) 181e7b1406cSJoe Komlodi FIELD(INTR_STATUS, CMD_QUEUE_RDY, 3, 1) 182e7b1406cSJoe Komlodi FIELD(INTR_STATUS, RESP_RDY, 4, 1) 183e7b1406cSJoe Komlodi FIELD(INTR_STATUS, TRANSFER_ABORT, 5, 1) 184e7b1406cSJoe Komlodi FIELD(INTR_STATUS, CCC_UPDATED, 6, 1) 185e7b1406cSJoe Komlodi FIELD(INTR_STATUS, DYN_ADDR_ASSGN, 8, 1) 186e7b1406cSJoe Komlodi FIELD(INTR_STATUS, TRANSFER_ERR, 9, 1) 187e7b1406cSJoe Komlodi FIELD(INTR_STATUS, DEFSLV, 10, 1) 188e7b1406cSJoe Komlodi FIELD(INTR_STATUS, READ_REQ_RECV, 11, 1) 189e7b1406cSJoe Komlodi FIELD(INTR_STATUS, IBI_UPDATED, 12, 1) 190e7b1406cSJoe Komlodi FIELD(INTR_STATUS, BUSOWNER_UPDATED, 13, 1) 1917d87775fSJoe Komlodi REG32(INTR_STATUS_EN, 0x40) 192e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, TX_THLD, 0, 1) 193e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, RX_THLD, 1, 1) 194e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, IBI_THLD, 2, 1) 195e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, CMD_QUEUE_RDY, 3, 1) 196e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, RESP_RDY, 4, 1) 197e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, TRANSFER_ABORT, 5, 1) 198e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, CCC_UPDATED, 6, 1) 199e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, DYN_ADDR_ASSGN, 8, 1) 200e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, TRANSFER_ERR, 9, 1) 201e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, DEFSLV, 10, 1) 202e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, READ_REQ_RECV, 11, 1) 203e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, IBI_UPDATED, 12, 1) 204e7b1406cSJoe Komlodi FIELD(INTR_STATUS_EN, BUSOWNER_UPDATED, 13, 1) 2057d87775fSJoe Komlodi REG32(INTR_SIGNAL_EN, 0x44) 206e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, TX_THLD, 0, 1) 207e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, RX_THLD, 1, 1) 208e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, IBI_THLD, 2, 1) 209e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, CMD_QUEUE_RDY, 3, 1) 210e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, RESP_RDY, 4, 1) 211e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, TRANSFER_ABORT, 5, 1) 212e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, CCC_UPDATED, 6, 1) 213e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, DYN_ADDR_ASSGN, 8, 1) 214e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, TRANSFER_ERR, 9, 1) 215e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, DEFSLV, 10, 1) 216e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, READ_REQ_RECV, 11, 1) 217e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, IBI_UPDATED, 12, 1) 218e7b1406cSJoe Komlodi FIELD(INTR_SIGNAL_EN, BUSOWNER_UPDATED, 13, 1) 2197d87775fSJoe Komlodi REG32(INTR_FORCE, 0x48) 220e7b1406cSJoe Komlodi FIELD(INTR_FORCE, TX_THLD, 0, 1) 221e7b1406cSJoe Komlodi FIELD(INTR_FORCE, RX_THLD, 1, 1) 222e7b1406cSJoe Komlodi FIELD(INTR_FORCE, IBI_THLD, 2, 1) 223e7b1406cSJoe Komlodi FIELD(INTR_FORCE, CMD_QUEUE_RDY, 3, 1) 224e7b1406cSJoe Komlodi FIELD(INTR_FORCE, RESP_RDY, 4, 1) 225e7b1406cSJoe Komlodi FIELD(INTR_FORCE, TRANSFER_ABORT, 5, 1) 226e7b1406cSJoe Komlodi FIELD(INTR_FORCE, CCC_UPDATED, 6, 1) 227e7b1406cSJoe Komlodi FIELD(INTR_FORCE, DYN_ADDR_ASSGN, 8, 1) 228e7b1406cSJoe Komlodi FIELD(INTR_FORCE, TRANSFER_ERR, 9, 1) 229e7b1406cSJoe Komlodi FIELD(INTR_FORCE, DEFSLV, 10, 1) 230e7b1406cSJoe Komlodi FIELD(INTR_FORCE, READ_REQ_RECV, 11, 1) 231e7b1406cSJoe Komlodi FIELD(INTR_FORCE, IBI_UPDATED, 12, 1) 232e7b1406cSJoe Komlodi FIELD(INTR_FORCE, BUSOWNER_UPDATED, 13, 1) 2337d87775fSJoe Komlodi REG32(QUEUE_STATUS_LEVEL, 0x4c) 234e7b1406cSJoe Komlodi FIELD(QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, 0, 8) 235e7b1406cSJoe Komlodi FIELD(QUEUE_STATUS_LEVEL, RESP_BUF_BLR, 8, 8) 236e7b1406cSJoe Komlodi FIELD(QUEUE_STATUS_LEVEL, IBI_BUF_BLR, 16, 8) 237e7b1406cSJoe Komlodi FIELD(QUEUE_STATUS_LEVEL, IBI_STATUS_CNT, 24, 5) 2387d87775fSJoe Komlodi REG32(DATA_BUFFER_STATUS_LEVEL, 0x50) 239e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 0, 8) 240e7b1406cSJoe Komlodi FIELD(DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, 16, 8) 2417d87775fSJoe Komlodi REG32(PRESENT_STATE, 0x54) 242e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, SCL_LINE_SIGNAL_LEVEL, 0, 1) 243e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, SDA_LINE_SIGNAL_LEVEL, 1, 1) 244e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, CURRENT_MASTER, 2, 1) 245e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, CM_TFR_STATUS, 8, 6) 246e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, CM_TFR_ST_STATUS, 16, 6) 247e7b1406cSJoe Komlodi FIELD(PRESENT_STATE, CMD_TID, 24, 4) 2487d87775fSJoe Komlodi REG32(CCC_DEVICE_STATUS, 0x58) 249e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, PENDING_INTR, 0, 4) 250e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, PROTOCOL_ERR, 4, 2) 251e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, ACTIVITY_MODE, 6, 2) 252e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, UNDER_ERR, 8, 1) 253e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, SLV_BUSY, 9, 1) 254e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, OVERFLOW_ERR, 10, 1) 255e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, DATA_NOT_READY, 11, 1) 256e7b1406cSJoe Komlodi FIELD(CCC_DEVICE_STATUS, BUFFER_NOT_AVAIL, 12, 1) 2577d87775fSJoe Komlodi REG32(DEVICE_ADDR_TABLE_POINTER, 0x5c) 2587d87775fSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16) 2597d87775fSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR, 0, 16) 2607d87775fSJoe Komlodi REG32(DEV_CHAR_TABLE_POINTER, 0x60) 261e7b1406cSJoe Komlodi FIELD(DEV_CHAR_TABLE_POINTER, P_DEV_CHAR_TABLE_START_ADDR, 0, 12) 262e7b1406cSJoe Komlodi FIELD(DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH, 12, 7) 263e7b1406cSJoe Komlodi FIELD(DEV_CHAR_TABLE_POINTER, PRESENT_DEV_CHAR_TABLE_INDEX, 19, 3) 2647d87775fSJoe Komlodi REG32(VENDOR_SPECIFIC_REG_POINTER, 0x6c) 265e7b1406cSJoe Komlodi FIELD(VENDOR_SPECIFIC_REG_POINTER, P_VENDOR_REG_START_ADDR, 0, 16) 2667d87775fSJoe Komlodi REG32(SLV_MIPI_PID_VALUE, 0x70) 2677d87775fSJoe Komlodi REG32(SLV_PID_VALUE, 0x74) 268e7b1406cSJoe Komlodi FIELD(SLV_PID_VALUE, SLV_PID_DCR, 0, 12) 269e7b1406cSJoe Komlodi FIELD(SLV_PID_VALUE, SLV_INST_ID, 12, 4) 270e7b1406cSJoe Komlodi FIELD(SLV_PID_VALUE, SLV_PART_ID, 16, 16) 2717d87775fSJoe Komlodi REG32(SLV_CHAR_CTRL, 0x78) 272e7b1406cSJoe Komlodi FIELD(SLV_CHAR_CTRL, BCR, 0, 8) 273e7b1406cSJoe Komlodi FIELD(SLV_CHAR_CTRL, DCR, 8, 8) 274e7b1406cSJoe Komlodi FIELD(SLV_CHAR_CTRL, HDR_CAP, 16, 8) 2757d87775fSJoe Komlodi REG32(SLV_MAX_LEN, 0x7c) 276e7b1406cSJoe Komlodi FIELD(SLV_MAX_LEN, MWL, 0, 16) 277e7b1406cSJoe Komlodi FIELD(SLV_MAX_LEN, MRL, 16, 16) 2787d87775fSJoe Komlodi REG32(MAX_READ_TURNAROUND, 0x80) 2797d87775fSJoe Komlodi REG32(MAX_DATA_SPEED, 0x84) 2807d87775fSJoe Komlodi REG32(SLV_DEBUG_STATUS, 0x88) 2817d87775fSJoe Komlodi REG32(SLV_INTR_REQ, 0x8c) 282e7b1406cSJoe Komlodi FIELD(SLV_INTR_REQ, SIR, 0, 1) 283e7b1406cSJoe Komlodi FIELD(SLV_INTR_REQ, SIR_CTRL, 1, 2) 284e7b1406cSJoe Komlodi FIELD(SLV_INTR_REQ, MIR, 3, 1) 285e7b1406cSJoe Komlodi FIELD(SLV_INTR_REQ, IBI_STS, 8, 2) 286e7b1406cSJoe Komlodi REG32(SLV_TSX_SYMBL_TIMING, 0x90) 287e7b1406cSJoe Komlodi FIELD(SLV_TSX_SYMBL_TIMING, SLV_TSX_SYMBL_CNT, 0, 6) 2887d87775fSJoe Komlodi REG32(DEVICE_CTRL_EXTENDED, 0xb0) 289e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL_EXTENDED, MODE, 0, 2) 290e7b1406cSJoe Komlodi FIELD(DEVICE_CTRL_EXTENDED, REQMST_ACK_CTRL, 3, 1) 2917d87775fSJoe Komlodi REG32(SCL_I3C_OD_TIMING, 0xb4) 292e7b1406cSJoe Komlodi FIELD(SCL_I3C_OD_TIMING, I3C_OD_LCNT, 0, 8) 293e7b1406cSJoe Komlodi FIELD(SCL_I3C_OD_TIMING, I3C_OD_HCNT, 16, 8) 2947d87775fSJoe Komlodi REG32(SCL_I3C_PP_TIMING, 0xb8) 295e7b1406cSJoe Komlodi FIELD(SCL_I3C_PP_TIMING, I3C_PP_LCNT, 0, 8) 296e7b1406cSJoe Komlodi FIELD(SCL_I3C_PP_TIMING, I3C_PP_HCNT, 16, 8) 2977d87775fSJoe Komlodi REG32(SCL_I2C_FM_TIMING, 0xbc) 2987d87775fSJoe Komlodi REG32(SCL_I2C_FMP_TIMING, 0xc0) 299e7b1406cSJoe Komlodi FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_LCNT, 0, 16) 300e7b1406cSJoe Komlodi FIELD(SCL_I2C_FMP_TIMING, I2C_FMP_HCNT, 16, 8) 3017d87775fSJoe Komlodi REG32(SCL_EXT_LCNT_TIMING, 0xc8) 3027d87775fSJoe Komlodi REG32(SCL_EXT_TERMN_LCNT_TIMING, 0xcc) 3037d87775fSJoe Komlodi REG32(BUS_FREE_TIMING, 0xd4) 3047d87775fSJoe Komlodi REG32(BUS_IDLE_TIMING, 0xd8) 305e7b1406cSJoe Komlodi FIELD(BUS_IDLE_TIMING, BUS_IDLE_TIME, 0, 20) 3067d87775fSJoe Komlodi REG32(I3C_VER_ID, 0xe0) 3077d87775fSJoe Komlodi REG32(I3C_VER_TYPE, 0xe4) 3087d87775fSJoe Komlodi REG32(EXTENDED_CAPABILITY, 0xe8) 309e7b1406cSJoe Komlodi FIELD(EXTENDED_CAPABILITY, APP_IF_MODE, 0, 2) 310e7b1406cSJoe Komlodi FIELD(EXTENDED_CAPABILITY, APP_IF_DATA_WIDTH, 2, 2) 311e7b1406cSJoe Komlodi FIELD(EXTENDED_CAPABILITY, OPERATION_MODE, 4, 2) 312e7b1406cSJoe Komlodi FIELD(EXTENDED_CAPABILITY, CLK_PERIOD, 8, 6) 3137d87775fSJoe Komlodi REG32(SLAVE_CONFIG, 0xec) 314e7b1406cSJoe Komlodi FIELD(SLAVE_CONFIG, DMA_EN, 0, 1) 315e7b1406cSJoe Komlodi FIELD(SLAVE_CONFIG, HJ_CAP, 0, 1) 316e7b1406cSJoe Komlodi FIELD(SLAVE_CONFIG, CLK_PERIOD, 2, 14) 317e7b1406cSJoe Komlodi /* Device characteristic table fields */ 318e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC1, 0x200) 319e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, 0x200) 320e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DYNAMIC_ADDR, 0, 8) 321e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, DCR, 8, 8) 322e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, BCR, 16, 8) 323e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC_SECONDARY, STATIC_ADDR, 24, 8) 324e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC2, 0x204) 325e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC2, MSB_PID, 0, 16) 326e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC3, 0x208) 327e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 0, 8) 328e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 8, 8) 329e7b1406cSJoe Komlodi REG32(DEVICE_CHARACTERISTIC_TABLE_LOC4, 0x20c) 330e7b1406cSJoe Komlodi FIELD(DEVICE_CHARACTERISTIC_TABLE_LOC4, DEV_DYNAMIC_ADDR, 0, 8) 331e7b1406cSJoe Komlodi /* Dev addr table fields */ 332e7b1406cSJoe Komlodi REG32(DEVICE_ADDR_TABLE_LOC1, 0x280) 333e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_STATIC_ADDR, 0, 7) 334e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_PEC_EN, 11, 1) 335e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_WITH_DATA, 12, 1) 336e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, SIR_REJECT, 13, 1) 337e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, MR_REJECT, 14, 1) 338e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_DYNAMIC_ADDR, 16, 8) 339e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, IBI_ADDR_MASK, 24, 2) 340e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, DEV_NACK_RETRY_CNT, 29, 2) 341e7b1406cSJoe Komlodi FIELD(DEVICE_ADDR_TABLE_LOC1, LEGACY_I2C_DEVICE, 31, 1) 3427d87775fSJoe Komlodi 343*eee51e8fSJoe Komlodi static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s); 344*eee51e8fSJoe Komlodi 345a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_controller_ro[ASPEED_I3C_DEVICE_NR_REGS] = { 346a9d3f922SJoe Komlodi [R_I3C1_REG0] = 0xfc000000, 347a9d3f922SJoe Komlodi [R_I3C1_REG1] = 0xfff00000, 348a9d3f922SJoe Komlodi [R_I3C2_REG0] = 0xfc000000, 349a9d3f922SJoe Komlodi [R_I3C2_REG1] = 0xfff00000, 350a9d3f922SJoe Komlodi [R_I3C3_REG0] = 0xfc000000, 351a9d3f922SJoe Komlodi [R_I3C3_REG1] = 0xfff00000, 352a9d3f922SJoe Komlodi [R_I3C4_REG0] = 0xfc000000, 353a9d3f922SJoe Komlodi [R_I3C4_REG1] = 0xfff00000, 354a9d3f922SJoe Komlodi [R_I3C5_REG0] = 0xfc000000, 355a9d3f922SJoe Komlodi [R_I3C5_REG1] = 0xfff00000, 356a9d3f922SJoe Komlodi [R_I3C6_REG0] = 0xfc000000, 357a9d3f922SJoe Komlodi [R_I3C6_REG1] = 0xfff00000, 358a9d3f922SJoe Komlodi }; 359a9d3f922SJoe Komlodi 3607d87775fSJoe Komlodi static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = { 3617d87775fSJoe Komlodi [R_HW_CAPABILITY] = 0x000e00bf, 3627d87775fSJoe Komlodi [R_QUEUE_THLD_CTRL] = 0x01000101, 3631032d6d6SJoe Komlodi [R_DATA_BUFFER_THLD_CTRL] = 0x01010100, 3641032d6d6SJoe Komlodi [R_SLV_EVENT_CTRL] = 0x0000000b, 3651032d6d6SJoe Komlodi [R_QUEUE_STATUS_LEVEL] = 0x00000002, 3661032d6d6SJoe Komlodi [R_DATA_BUFFER_STATUS_LEVEL] = 0x00000010, 3671032d6d6SJoe Komlodi [R_PRESENT_STATE] = 0x00000003, 3687d87775fSJoe Komlodi [R_I3C_VER_ID] = 0x3130302a, 3697d87775fSJoe Komlodi [R_I3C_VER_TYPE] = 0x6c633033, 3707d87775fSJoe Komlodi [R_DEVICE_ADDR_TABLE_POINTER] = 0x00080280, 3717d87775fSJoe Komlodi [R_DEV_CHAR_TABLE_POINTER] = 0x00020200, 3721032d6d6SJoe Komlodi [R_SLV_CHAR_CTRL] = 0x00010000, 3737d87775fSJoe Komlodi [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0, 3747d87775fSJoe Komlodi [R_SLV_MAX_LEN] = 0x00ff00ff, 3751032d6d6SJoe Komlodi [R_SLV_TSX_SYMBL_TIMING] = 0x0000003f, 3761032d6d6SJoe Komlodi [R_SCL_I3C_OD_TIMING] = 0x000a0010, 3771032d6d6SJoe Komlodi [R_SCL_I3C_PP_TIMING] = 0x000a000a, 3781032d6d6SJoe Komlodi [R_SCL_I2C_FM_TIMING] = 0x00100010, 3791032d6d6SJoe Komlodi [R_SCL_I2C_FMP_TIMING] = 0x00100010, 3801032d6d6SJoe Komlodi [R_SCL_EXT_LCNT_TIMING] = 0x20202020, 3811032d6d6SJoe Komlodi [R_SCL_EXT_TERMN_LCNT_TIMING] = 0x00300000, 3821032d6d6SJoe Komlodi [R_BUS_FREE_TIMING] = 0x00200020, 3831032d6d6SJoe Komlodi [R_BUS_IDLE_TIMING] = 0x00000020, 3841032d6d6SJoe Komlodi [R_EXTENDED_CAPABILITY] = 0x00000239, 3851032d6d6SJoe Komlodi [R_SLAVE_CONFIG] = 0x00000023, 3867d87775fSJoe Komlodi }; 3877d87775fSJoe Komlodi 388a9d3f922SJoe Komlodi static const uint32_t ast2600_i3c_device_ro[ASPEED_I3C_DEVICE_NR_REGS] = { 389a9d3f922SJoe Komlodi [R_DEVICE_CTRL] = 0x04fffe00, 390a9d3f922SJoe Komlodi [R_DEVICE_ADDR] = 0x7f807f80, 391a9d3f922SJoe Komlodi [R_HW_CAPABILITY] = 0xffffffff, 392a9d3f922SJoe Komlodi [R_IBI_QUEUE_STATUS] = 0xffffffff, 393a9d3f922SJoe Komlodi [R_DATA_BUFFER_THLD_CTRL] = 0xf8f8f8f8, 394a9d3f922SJoe Komlodi [R_IBI_QUEUE_CTRL] = 0xfffffff0, 395a9d3f922SJoe Komlodi [R_RESET_CTRL] = 0xffffffc0, 396a9d3f922SJoe Komlodi [R_SLV_EVENT_CTRL] = 0xffffff3f, 397a9d3f922SJoe Komlodi [R_INTR_STATUS] = 0xffff809f, 398a9d3f922SJoe Komlodi [R_INTR_STATUS_EN] = 0xffff8080, 399a9d3f922SJoe Komlodi [R_INTR_SIGNAL_EN] = 0xffff8080, 400a9d3f922SJoe Komlodi [R_INTR_FORCE] = 0xffff8000, 401a9d3f922SJoe Komlodi [R_QUEUE_STATUS_LEVEL] = 0xffffffff, 402a9d3f922SJoe Komlodi [R_DATA_BUFFER_STATUS_LEVEL] = 0xffffffff, 403a9d3f922SJoe Komlodi [R_PRESENT_STATE] = 0xffffffff, 404a9d3f922SJoe Komlodi [R_CCC_DEVICE_STATUS] = 0xffffffff, 405a9d3f922SJoe Komlodi [R_I3C_VER_ID] = 0xffffffff, 406a9d3f922SJoe Komlodi [R_I3C_VER_TYPE] = 0xffffffff, 407a9d3f922SJoe Komlodi [R_DEVICE_ADDR_TABLE_POINTER] = 0xffffffff, 408a9d3f922SJoe Komlodi [R_DEV_CHAR_TABLE_POINTER] = 0xffcbffff, 409a9d3f922SJoe Komlodi [R_SLV_PID_VALUE] = 0xffff0fff, 410a9d3f922SJoe Komlodi [R_SLV_CHAR_CTRL] = 0xffffffff, 411a9d3f922SJoe Komlodi [A_VENDOR_SPECIFIC_REG_POINTER] = 0xffffffff, 412a9d3f922SJoe Komlodi [R_SLV_MAX_LEN] = 0xffffffff, 413a9d3f922SJoe Komlodi [R_MAX_READ_TURNAROUND] = 0xffffffff, 414a9d3f922SJoe Komlodi [R_MAX_DATA_SPEED] = 0xffffffff, 415a9d3f922SJoe Komlodi [R_SLV_INTR_REQ] = 0xfffffff0, 416a9d3f922SJoe Komlodi [R_SLV_TSX_SYMBL_TIMING] = 0xffffffc0, 417a9d3f922SJoe Komlodi [R_DEVICE_CTRL_EXTENDED] = 0xfffffff8, 418a9d3f922SJoe Komlodi [R_SCL_I3C_OD_TIMING] = 0xff00ff00, 419a9d3f922SJoe Komlodi [R_SCL_I3C_PP_TIMING] = 0xff00ff00, 420a9d3f922SJoe Komlodi [R_SCL_I2C_FMP_TIMING] = 0xff000000, 421a9d3f922SJoe Komlodi [R_SCL_EXT_TERMN_LCNT_TIMING] = 0x0000fff0, 422a9d3f922SJoe Komlodi [R_BUS_IDLE_TIMING] = 0xfff00000, 423a9d3f922SJoe Komlodi [R_EXTENDED_CAPABILITY] = 0xffffffff, 424a9d3f922SJoe Komlodi [R_SLAVE_CONFIG] = 0xffffffff, 425a9d3f922SJoe Komlodi }; 426a9d3f922SJoe Komlodi 4274ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_entdaa(AspeedI3CDevice *s) 4284ba25376SJoe Komlodi { 4294ba25376SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, ENTDAA); 4304ba25376SJoe Komlodi } 4314ba25376SJoe Komlodi 4324ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ts(AspeedI3CDevice *s) 4334ba25376SJoe Komlodi { 4344ba25376SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_TS); 4354ba25376SJoe Komlodi } 4364ba25376SJoe Komlodi 4374ba25376SJoe Komlodi static inline bool aspeed_i3c_device_has_hdr_ddr(AspeedI3CDevice *s) 4384ba25376SJoe Komlodi { 4394ba25376SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, HW_CAPABILITY, HDR_DDR); 4404ba25376SJoe Komlodi } 4414ba25376SJoe Komlodi 4424ba25376SJoe Komlodi static inline bool aspeed_i3c_device_can_transmit(AspeedI3CDevice *s) 4434ba25376SJoe Komlodi { 4444ba25376SJoe Komlodi /* 4454ba25376SJoe Komlodi * We can only transmit if we're enabled and the resume bit is cleared. 4464ba25376SJoe Komlodi * The resume bit is set on a transaction error, and software must clear it. 4474ba25376SJoe Komlodi */ 4484ba25376SJoe Komlodi return ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_EN) && 4494ba25376SJoe Komlodi !ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_RESUME); 4504ba25376SJoe Komlodi } 4514ba25376SJoe Komlodi 4524ba25376SJoe Komlodi static inline uint8_t aspeed_i3c_device_fifo_threshold_from_reg(uint8_t regval) 4534ba25376SJoe Komlodi { 4544ba25376SJoe Komlodi return regval = regval ? (2 << regval) : 1; 4554ba25376SJoe Komlodi } 4564ba25376SJoe Komlodi 45793ec6949SJoe Komlodi static inline uint8_t aspeed_i3c_device_ibi_slice_size(AspeedI3CDevice *s) 45893ec6949SJoe Komlodi { 45993ec6949SJoe Komlodi uint8_t ibi_slice_size = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 46093ec6949SJoe Komlodi IBI_DATA_THLD); 46193ec6949SJoe Komlodi /* The minimum supported slice size is 4 bytes. */ 46293ec6949SJoe Komlodi if (ibi_slice_size == 0) { 46393ec6949SJoe Komlodi ibi_slice_size = 1; 46493ec6949SJoe Komlodi } 46593ec6949SJoe Komlodi ibi_slice_size *= sizeof(uint32_t); 46693ec6949SJoe Komlodi /* maximum supported size is 63 bytes. */ 46793ec6949SJoe Komlodi if (ibi_slice_size >= 64) { 46893ec6949SJoe Komlodi ibi_slice_size = 63; 46993ec6949SJoe Komlodi } 47093ec6949SJoe Komlodi 47193ec6949SJoe Komlodi return ibi_slice_size; 47293ec6949SJoe Komlodi } 47393ec6949SJoe Komlodi 4743816bedaSJoe Komlodi static void aspeed_i3c_device_update_irq(AspeedI3CDevice *s) 4753816bedaSJoe Komlodi { 4763816bedaSJoe Komlodi bool level = !!(s->regs[R_INTR_SIGNAL_EN] & s->regs[R_INTR_STATUS]); 4773816bedaSJoe Komlodi qemu_set_irq(s->irq, level); 4783816bedaSJoe Komlodi } 4793816bedaSJoe Komlodi 4804ba25376SJoe Komlodi static void aspeed_i3c_device_end_transfer(AspeedI3CDevice *s, bool is_i2c) 4814ba25376SJoe Komlodi { 4824ba25376SJoe Komlodi if (is_i2c) { 4834ba25376SJoe Komlodi legacy_i2c_end_transfer(s->bus); 4844ba25376SJoe Komlodi } else { 4854ba25376SJoe Komlodi i3c_end_transfer(s->bus); 4864ba25376SJoe Komlodi } 4874ba25376SJoe Komlodi } 4884ba25376SJoe Komlodi 4894ba25376SJoe Komlodi static int aspeed_i3c_device_send_start(AspeedI3CDevice *s, uint8_t addr, 4904ba25376SJoe Komlodi bool is_recv, bool is_i2c) 4914ba25376SJoe Komlodi { 4924ba25376SJoe Komlodi int ret; 4934ba25376SJoe Komlodi 4944ba25376SJoe Komlodi if (is_i2c) { 4954ba25376SJoe Komlodi ret = legacy_i2c_start_transfer(s->bus, addr, is_recv); 4964ba25376SJoe Komlodi } else { 4974ba25376SJoe Komlodi ret = i3c_start_transfer(s->bus, addr, is_recv); 4984ba25376SJoe Komlodi } 4994ba25376SJoe Komlodi if (ret) { 5004ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed on TX with addr 0x%.2x\n", 5014ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s)), addr); 5024ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 5034ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 5044ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 5054ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_HALT); 5064ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); 5074ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); 5084ba25376SJoe Komlodi } 5094ba25376SJoe Komlodi 5104ba25376SJoe Komlodi return ret; 5114ba25376SJoe Komlodi } 5124ba25376SJoe Komlodi 5134ba25376SJoe Komlodi static int aspeed_i3c_device_send(AspeedI3CDevice *s, const uint8_t *data, 5144ba25376SJoe Komlodi uint32_t num_to_send, uint32_t *num_sent, 5154ba25376SJoe Komlodi bool is_i2c) 5164ba25376SJoe Komlodi { 5174ba25376SJoe Komlodi int ret; 5184ba25376SJoe Komlodi uint32_t i; 5194ba25376SJoe Komlodi 5204ba25376SJoe Komlodi *num_sent = 0; 5214ba25376SJoe Komlodi if (is_i2c) { 5224ba25376SJoe Komlodi /* Legacy I2C must be byte-by-byte. */ 5234ba25376SJoe Komlodi for (i = 0; i < num_to_send; i++) { 5244ba25376SJoe Komlodi ret = legacy_i2c_send(s->bus, data[i]); 5254ba25376SJoe Komlodi if (ret) { 5264ba25376SJoe Komlodi break; 5274ba25376SJoe Komlodi } 5284ba25376SJoe Komlodi (*num_sent)++; 5294ba25376SJoe Komlodi } 5304ba25376SJoe Komlodi } else { 5314ba25376SJoe Komlodi ret = i3c_send(s->bus, data, num_to_send, num_sent); 5324ba25376SJoe Komlodi } 5334ba25376SJoe Komlodi if (ret) { 5344ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed sending byte 0x%.2x\n", 5354ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s)), data[*num_sent]); 5364ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 5374ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 5384ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 5394ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_HALT); 5404ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); 5414ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); 5424ba25376SJoe Komlodi } 5434ba25376SJoe Komlodi 5444ba25376SJoe Komlodi trace_aspeed_i3c_device_send(s->id, *num_sent); 5454ba25376SJoe Komlodi 5464ba25376SJoe Komlodi return ret; 5474ba25376SJoe Komlodi } 5484ba25376SJoe Komlodi 5494ba25376SJoe Komlodi static int aspeed_i3c_device_send_byte(AspeedI3CDevice *s, uint8_t byte, 5504ba25376SJoe Komlodi bool is_i2c) 5514ba25376SJoe Komlodi { 5524ba25376SJoe Komlodi /* 5534ba25376SJoe Komlodi * Ignored, the caller will know if we sent 0 or 1 bytes depending on if 5544ba25376SJoe Komlodi * we were ACKed/NACKed. 5554ba25376SJoe Komlodi */ 5564ba25376SJoe Komlodi uint32_t num_sent; 5574ba25376SJoe Komlodi return aspeed_i3c_device_send(s, &byte, 1, &num_sent, is_i2c); 5584ba25376SJoe Komlodi } 5594ba25376SJoe Komlodi 5604ba25376SJoe Komlodi static int aspeed_i3c_device_recv_data(AspeedI3CDevice *s, bool is_i2c, 5614ba25376SJoe Komlodi uint8_t *data, uint16_t num_to_read, 5624ba25376SJoe Komlodi uint32_t *num_read) 5634ba25376SJoe Komlodi { 5644ba25376SJoe Komlodi int ret; 5654ba25376SJoe Komlodi 5664ba25376SJoe Komlodi if (is_i2c) { 5674ba25376SJoe Komlodi for (uint16_t i = 0; i < num_to_read; i++) { 5684ba25376SJoe Komlodi data[i] = legacy_i2c_recv(s->bus); 5694ba25376SJoe Komlodi } 5704ba25376SJoe Komlodi /* I2C devices can neither NACK a read, nor end transfers early. */ 5714ba25376SJoe Komlodi *num_read = num_to_read; 5724ba25376SJoe Komlodi trace_aspeed_i3c_device_recv_data(s->id, *num_read); 5734ba25376SJoe Komlodi return 0; 5744ba25376SJoe Komlodi } 5754ba25376SJoe Komlodi /* I3C devices can NACK if the controller sends an unsupported CCC. */ 5764ba25376SJoe Komlodi ret = i3c_recv(s->bus, data, num_to_read, num_read); 5774ba25376SJoe Komlodi if (ret) { 5784ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n", 5794ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 5804ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 5814ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 5824ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 5834ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_HALT); 5844ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ERR, 1); 5854ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEVICE_CTRL, I3C_RESUME, 1); 5864ba25376SJoe Komlodi } 5874ba25376SJoe Komlodi 5884ba25376SJoe Komlodi trace_aspeed_i3c_device_recv_data(s->id, *num_read); 5894ba25376SJoe Komlodi 5904ba25376SJoe Komlodi return ret; 5914ba25376SJoe Komlodi } 5924ba25376SJoe Komlodi 593*eee51e8fSJoe Komlodi static inline void aspeed_i3c_device_ctrl_w(AspeedI3CDevice *s, 594*eee51e8fSJoe Komlodi uint32_t val) 595*eee51e8fSJoe Komlodi { 596*eee51e8fSJoe Komlodi /* 597*eee51e8fSJoe Komlodi * If the user is setting I3C_RESUME, the controller was halted. 598*eee51e8fSJoe Komlodi * Try and resume execution and leave the bit cleared. 599*eee51e8fSJoe Komlodi */ 600*eee51e8fSJoe Komlodi if (FIELD_EX32(val, DEVICE_CTRL, I3C_RESUME)) { 601*eee51e8fSJoe Komlodi aspeed_i3c_device_cmd_queue_execute(s); 602*eee51e8fSJoe Komlodi val = FIELD_DP32(val, DEVICE_CTRL, I3C_RESUME, 0); 603*eee51e8fSJoe Komlodi } 604*eee51e8fSJoe Komlodi /* 605*eee51e8fSJoe Komlodi * I3C_ABORT being set sends an I3C STOP. It's cleared when the STOP is 606*eee51e8fSJoe Komlodi * sent. 607*eee51e8fSJoe Komlodi */ 608*eee51e8fSJoe Komlodi if (FIELD_EX32(val, DEVICE_CTRL, I3C_ABORT)) { 609*eee51e8fSJoe Komlodi aspeed_i3c_device_end_transfer(s, /*is_i2c=*/true); 610*eee51e8fSJoe Komlodi aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false); 611*eee51e8fSJoe Komlodi val = FIELD_DP32(val, DEVICE_CTRL, I3C_ABORT, 0); 612*eee51e8fSJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TRANSFER_ABORT, 1); 613*eee51e8fSJoe Komlodi aspeed_i3c_device_update_irq(s); 614*eee51e8fSJoe Komlodi } 615*eee51e8fSJoe Komlodi /* Update present state. */ 616*eee51e8fSJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 617*eee51e8fSJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 618*eee51e8fSJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 619*eee51e8fSJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_IDLE); 620*eee51e8fSJoe Komlodi 621*eee51e8fSJoe Komlodi s->regs[R_DEVICE_CTRL] = val; 622*eee51e8fSJoe Komlodi } 623*eee51e8fSJoe Komlodi 6244ba25376SJoe Komlodi static inline bool aspeed_i3c_device_target_is_i2c(AspeedI3CDevice *s, 6254ba25376SJoe Komlodi uint16_t offset) 6264ba25376SJoe Komlodi { 6274ba25376SJoe Komlodi uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset; 6284ba25376SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, 6294ba25376SJoe Komlodi LEGACY_I2C_DEVICE); 6304ba25376SJoe Komlodi } 6314ba25376SJoe Komlodi 6324ba25376SJoe Komlodi static uint8_t aspeed_i3c_device_target_addr(AspeedI3CDevice *s, 6334ba25376SJoe Komlodi uint16_t offset) 6344ba25376SJoe Komlodi { 6354ba25376SJoe Komlodi if (offset > ASPEED_I3C_NR_DEVICES) { 6364ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Device addr table offset %d out of " 6374ba25376SJoe Komlodi "bounds\n", object_get_canonical_path(OBJECT(s)), offset); 6384ba25376SJoe Komlodi /* If we're out of bounds, return an address of 0. */ 6394ba25376SJoe Komlodi return 0; 6404ba25376SJoe Komlodi } 6414ba25376SJoe Komlodi 6424ba25376SJoe Komlodi uint16_t dev_index = R_DEVICE_ADDR_TABLE_LOC1 + offset; 6434ba25376SJoe Komlodi /* I2C devices use a static address. */ 6444ba25376SJoe Komlodi if (aspeed_i3c_device_target_is_i2c(s, offset)) { 6454ba25376SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, 6464ba25376SJoe Komlodi DEV_STATIC_ADDR); 6474ba25376SJoe Komlodi } 6484ba25376SJoe Komlodi return FIELD_EX32(s->regs[dev_index], DEVICE_ADDR_TABLE_LOC1, 6494ba25376SJoe Komlodi DEV_DYNAMIC_ADDR); 6504ba25376SJoe Komlodi } 6514ba25376SJoe Komlodi 65293ec6949SJoe Komlodi static int aspeed_i3c_device_addr_table_index_from_addr(AspeedI3CDevice *s, 65393ec6949SJoe Komlodi uint8_t addr) 65493ec6949SJoe Komlodi { 65593ec6949SJoe Komlodi uint8_t table_size = ARRAY_FIELD_EX32(s->regs, DEVICE_ADDR_TABLE_POINTER, 65693ec6949SJoe Komlodi DEPTH); 65793ec6949SJoe Komlodi for (uint8_t i = 0; i < table_size; i++) { 65893ec6949SJoe Komlodi if (aspeed_i3c_device_target_addr(s, i) == addr) { 65993ec6949SJoe Komlodi return i; 66093ec6949SJoe Komlodi } 66193ec6949SJoe Komlodi } 66293ec6949SJoe Komlodi return -1; 66393ec6949SJoe Komlodi } 66493ec6949SJoe Komlodi 66593ec6949SJoe Komlodi static void aspeed_i3c_device_send_disec(AspeedI3CDevice *s) 66693ec6949SJoe Komlodi { 66793ec6949SJoe Komlodi uint8_t ccc = I3C_CCC_DISEC; 66893ec6949SJoe Komlodi if (s->ibi_data.send_direct_disec) { 66993ec6949SJoe Komlodi ccc = I3C_CCCD_DISEC; 67093ec6949SJoe Komlodi } 67193ec6949SJoe Komlodi 67293ec6949SJoe Komlodi aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, 67393ec6949SJoe Komlodi /*is_i2c=*/false); 67493ec6949SJoe Komlodi aspeed_i3c_device_send_byte(s, ccc, /*is_i2c=*/false); 67593ec6949SJoe Komlodi if (s->ibi_data.send_direct_disec) { 67693ec6949SJoe Komlodi aspeed_i3c_device_send_start(s, s->ibi_data.disec_addr, 67793ec6949SJoe Komlodi /*is_recv=*/false, /*is_i2c=*/false); 67893ec6949SJoe Komlodi } 67993ec6949SJoe Komlodi aspeed_i3c_device_send_byte(s, s->ibi_data.disec_byte, /*is_i2c=*/false); 68093ec6949SJoe Komlodi } 68193ec6949SJoe Komlodi 68293ec6949SJoe Komlodi static int aspeed_i3c_device_handle_hj(AspeedI3CDevice *s) 68393ec6949SJoe Komlodi { 68493ec6949SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_HOT_JOIN)) { 68593ec6949SJoe Komlodi s->ibi_data.notify_ibi_nack = true; 68693ec6949SJoe Komlodi } 68793ec6949SJoe Komlodi 68893ec6949SJoe Komlodi bool nack_and_disable = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, 68993ec6949SJoe Komlodi HOT_JOIN_ACK_NACK_CTRL); 69093ec6949SJoe Komlodi if (nack_and_disable) { 69193ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, 69293ec6949SJoe Komlodi IBI_QUEUE_STATUS, 69393ec6949SJoe Komlodi IBI_STATUS, 1); 69493ec6949SJoe Komlodi s->ibi_data.ibi_nacked = true; 69593ec6949SJoe Komlodi s->ibi_data.disec_byte = DISEC_HJ; 69693ec6949SJoe Komlodi return -1; 69793ec6949SJoe Komlodi } 69893ec6949SJoe Komlodi return 0; 69993ec6949SJoe Komlodi } 70093ec6949SJoe Komlodi 70193ec6949SJoe Komlodi static int aspeed_i3c_device_handle_ctlr_req(AspeedI3CDevice *s, uint8_t addr) 70293ec6949SJoe Komlodi { 70393ec6949SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_MASTER_REQ)) { 70493ec6949SJoe Komlodi s->ibi_data.notify_ibi_nack = true; 70593ec6949SJoe Komlodi } 70693ec6949SJoe Komlodi 70793ec6949SJoe Komlodi int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr); 70893ec6949SJoe Komlodi /* Doesn't exist in the table, NACK it, don't DISEC. */ 70993ec6949SJoe Komlodi if (table_offset < 0) { 71093ec6949SJoe Komlodi return -1; 71193ec6949SJoe Komlodi } 71293ec6949SJoe Komlodi 71393ec6949SJoe Komlodi table_offset += R_DEVICE_ADDR_TABLE_LOC1; 71493ec6949SJoe Komlodi if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, MR_REJECT)) { 71593ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, 71693ec6949SJoe Komlodi IBI_QUEUE_STATUS, 71793ec6949SJoe Komlodi IBI_STATUS, 1); 71893ec6949SJoe Komlodi s->ibi_data.ibi_nacked = true; 71993ec6949SJoe Komlodi s->ibi_data.disec_addr = addr; 72093ec6949SJoe Komlodi /* Tell the requester to disable controller role requests. */ 72193ec6949SJoe Komlodi s->ibi_data.disec_byte = DISEC_CR; 72293ec6949SJoe Komlodi s->ibi_data.send_direct_disec = true; 72393ec6949SJoe Komlodi return -1; 72493ec6949SJoe Komlodi } 72593ec6949SJoe Komlodi return 0; 72693ec6949SJoe Komlodi } 72793ec6949SJoe Komlodi 72893ec6949SJoe Komlodi static int aspeed_i3c_device_handle_targ_irq(AspeedI3CDevice *s, uint8_t addr) 72993ec6949SJoe Komlodi { 73093ec6949SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, IBI_QUEUE_CTRL, NOTIFY_REJECTED_SLAVE_IRQ)) { 73193ec6949SJoe Komlodi s->ibi_data.notify_ibi_nack = true; 73293ec6949SJoe Komlodi } 73393ec6949SJoe Komlodi 73493ec6949SJoe Komlodi int table_offset = aspeed_i3c_device_addr_table_index_from_addr(s, addr); 73593ec6949SJoe Komlodi /* Doesn't exist in the table, NACK it, don't DISEC. */ 73693ec6949SJoe Komlodi if (table_offset < 0) { 73793ec6949SJoe Komlodi return -1; 73893ec6949SJoe Komlodi } 73993ec6949SJoe Komlodi 74093ec6949SJoe Komlodi table_offset += R_DEVICE_ADDR_TABLE_LOC1; 74193ec6949SJoe Komlodi if (FIELD_EX32(s->regs[table_offset], DEVICE_ADDR_TABLE_LOC1, SIR_REJECT)) { 74293ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, 74393ec6949SJoe Komlodi IBI_QUEUE_STATUS, 74493ec6949SJoe Komlodi IBI_STATUS, 1); 74593ec6949SJoe Komlodi s->ibi_data.ibi_nacked = true; 74693ec6949SJoe Komlodi s->ibi_data.disec_addr = addr; 74793ec6949SJoe Komlodi /* Tell the requester to disable interrupts. */ 74893ec6949SJoe Komlodi s->ibi_data.disec_byte = DISEC_INT; 74993ec6949SJoe Komlodi s->ibi_data.send_direct_disec = true; 75093ec6949SJoe Komlodi return -1; 75193ec6949SJoe Komlodi } 75293ec6949SJoe Komlodi return 0; 75393ec6949SJoe Komlodi } 75493ec6949SJoe Komlodi 75593ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_handle(I3CBus *bus, I3CTarget *target, 75693ec6949SJoe Komlodi uint8_t addr, bool is_recv) 75793ec6949SJoe Komlodi { 75893ec6949SJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); 75993ec6949SJoe Komlodi 76093ec6949SJoe Komlodi trace_aspeed_i3c_device_ibi_handle(s->id, addr, is_recv); 76193ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, 76293ec6949SJoe Komlodi IBI_QUEUE_STATUS, IBI_ID, 76393ec6949SJoe Komlodi (addr << 1) | is_recv); 76493ec6949SJoe Komlodi /* Is this a hot join request? */ 76593ec6949SJoe Komlodi if (addr == I3C_HJ_ADDR) { 76693ec6949SJoe Komlodi return aspeed_i3c_device_handle_hj(s); 76793ec6949SJoe Komlodi } 76893ec6949SJoe Komlodi /* Is secondary controller requesting access? */ 76993ec6949SJoe Komlodi if (addr == target->address && !is_recv) { 77093ec6949SJoe Komlodi return aspeed_i3c_device_handle_ctlr_req(s, addr); 77193ec6949SJoe Komlodi } 77293ec6949SJoe Komlodi /* Is this a target IRQ? */ 77393ec6949SJoe Komlodi if (addr == target->address && is_recv) { 77493ec6949SJoe Komlodi return aspeed_i3c_device_handle_targ_irq(s, addr); 77593ec6949SJoe Komlodi } 77693ec6949SJoe Komlodi 77793ec6949SJoe Komlodi /* Not sure what this is, NACK it. */ 77893ec6949SJoe Komlodi return -1; 77993ec6949SJoe Komlodi } 78093ec6949SJoe Komlodi 78193ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_recv(I3CBus *bus, uint8_t data) 78293ec6949SJoe Komlodi { 78393ec6949SJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); 78493ec6949SJoe Komlodi if (fifo8_is_full(&s->ibi_data.ibi_intermediate_queue)) { 78593ec6949SJoe Komlodi return -1; 78693ec6949SJoe Komlodi } 78793ec6949SJoe Komlodi 78893ec6949SJoe Komlodi fifo8_push(&s->ibi_data.ibi_intermediate_queue, data); 78993ec6949SJoe Komlodi trace_aspeed_i3c_device_ibi_recv(s->id, data); 79093ec6949SJoe Komlodi return 0; 79193ec6949SJoe Komlodi } 79293ec6949SJoe Komlodi 79393ec6949SJoe Komlodi static void aspeed_i3c_device_ibi_queue_push(AspeedI3CDevice *s) 79493ec6949SJoe Komlodi { 79593ec6949SJoe Komlodi /* Stored value is in 32-bit chunks, convert it to byte chunks. */ 79693ec6949SJoe Komlodi uint8_t ibi_slice_size = aspeed_i3c_device_ibi_slice_size(s); 79793ec6949SJoe Komlodi uint8_t num_slices = fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) / 79893ec6949SJoe Komlodi ibi_slice_size; 79993ec6949SJoe Komlodi uint8_t ibi_status_count = num_slices; 80093ec6949SJoe Komlodi union { 80193ec6949SJoe Komlodi uint8_t b[sizeof(uint32_t)]; 80293ec6949SJoe Komlodi uint32_t val32; 80393ec6949SJoe Komlodi } ibi_data = { 80493ec6949SJoe Komlodi .val32 = 0 80593ec6949SJoe Komlodi }; 80693ec6949SJoe Komlodi 80793ec6949SJoe Komlodi /* The report was suppressed, do nothing. */ 80893ec6949SJoe Komlodi if (s->ibi_data.ibi_nacked && !s->ibi_data.notify_ibi_nack) { 80993ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 81093ec6949SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 81193ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 81293ec6949SJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_IDLE); 81393ec6949SJoe Komlodi return; 81493ec6949SJoe Komlodi } 81593ec6949SJoe Komlodi 81693ec6949SJoe Komlodi /* If we don't have any slices to push, just push the status. */ 81793ec6949SJoe Komlodi if (num_slices == 0) { 81893ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = 81993ec6949SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, 82093ec6949SJoe Komlodi LAST_STATUS, 1); 82193ec6949SJoe Komlodi fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status); 82293ec6949SJoe Komlodi ibi_status_count = 1; 82393ec6949SJoe Komlodi } 82493ec6949SJoe Komlodi 82593ec6949SJoe Komlodi for (uint8_t i = 0; i < num_slices; i++) { 82693ec6949SJoe Komlodi /* If this is the last slice, set LAST_STATUS. */ 82793ec6949SJoe Komlodi if (fifo8_num_used(&s->ibi_data.ibi_intermediate_queue) < 82893ec6949SJoe Komlodi ibi_slice_size) { 82993ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = 83093ec6949SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, 83193ec6949SJoe Komlodi IBI_DATA_LEN, 83293ec6949SJoe Komlodi fifo8_num_used(&s->ibi_data.ibi_intermediate_queue)); 83393ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = 83493ec6949SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, 83593ec6949SJoe Komlodi LAST_STATUS, 1); 83693ec6949SJoe Komlodi } else { 83793ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = 83893ec6949SJoe Komlodi FIELD_DP32(s->ibi_data.ibi_queue_status, IBI_QUEUE_STATUS, 83993ec6949SJoe Komlodi IBI_DATA_LEN, ibi_slice_size); 84093ec6949SJoe Komlodi } 84193ec6949SJoe Komlodi 84293ec6949SJoe Komlodi /* Push the IBI status header. */ 84393ec6949SJoe Komlodi fifo32_push(&s->ibi_queue, s->ibi_data.ibi_queue_status); 84493ec6949SJoe Komlodi /* Move each IBI byte into a 32-bit word and push it into the queue. */ 84593ec6949SJoe Komlodi for (uint8_t j = 0; j < ibi_slice_size; ++j) { 84693ec6949SJoe Komlodi if (fifo8_is_empty(&s->ibi_data.ibi_intermediate_queue)) { 84793ec6949SJoe Komlodi break; 84893ec6949SJoe Komlodi } 84993ec6949SJoe Komlodi 85093ec6949SJoe Komlodi ibi_data.b[j & 3] = fifo8_pop(&s->ibi_data.ibi_intermediate_queue); 85193ec6949SJoe Komlodi /* We have 32-bits, push it to the IBI FIFO. */ 85293ec6949SJoe Komlodi if ((j & 0x03) == 0x03) { 85393ec6949SJoe Komlodi fifo32_push(&s->ibi_queue, ibi_data.val32); 85493ec6949SJoe Komlodi ibi_data.val32 = 0; 85593ec6949SJoe Komlodi } 85693ec6949SJoe Komlodi } 85793ec6949SJoe Komlodi /* If the data isn't 32-bit aligned, push the leftover bytes. */ 85893ec6949SJoe Komlodi if (ibi_slice_size & 0x03) { 85993ec6949SJoe Komlodi fifo32_push(&s->ibi_queue, ibi_data.val32); 86093ec6949SJoe Komlodi } 86193ec6949SJoe Komlodi 86293ec6949SJoe Komlodi /* Clear out the data length for the next iteration. */ 86393ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = FIELD_DP32(s->ibi_data.ibi_queue_status, 86493ec6949SJoe Komlodi IBI_QUEUE_STATUS, IBI_DATA_LEN, 0); 86593ec6949SJoe Komlodi } 86693ec6949SJoe Komlodi 86793ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR, 86893ec6949SJoe Komlodi fifo32_num_used(&s->ibi_queue)); 86993ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_STATUS_CNT, 87093ec6949SJoe Komlodi ibi_status_count); 87193ec6949SJoe Komlodi /* Threshold is the register value + 1. */ 87293ec6949SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 87393ec6949SJoe Komlodi IBI_STATUS_THLD) + 1; 87493ec6949SJoe Komlodi if (fifo32_num_used(&s->ibi_queue) >= threshold) { 87593ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 1); 87693ec6949SJoe Komlodi aspeed_i3c_device_update_irq(s); 87793ec6949SJoe Komlodi } 87893ec6949SJoe Komlodi 87993ec6949SJoe Komlodi /* State update. */ 88093ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 88193ec6949SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 88293ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, 88393ec6949SJoe Komlodi ASPEED_I3C_TRANSFER_STATUS_IDLE); 88493ec6949SJoe Komlodi } 88593ec6949SJoe Komlodi 88693ec6949SJoe Komlodi static int aspeed_i3c_device_ibi_finish(I3CBus *bus) 88793ec6949SJoe Komlodi { 88893ec6949SJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(bus->qbus.parent); 88993ec6949SJoe Komlodi bool nack_and_disable_hj = ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, 89093ec6949SJoe Komlodi HOT_JOIN_ACK_NACK_CTRL); 89193ec6949SJoe Komlodi if (nack_and_disable_hj || s->ibi_data.send_direct_disec) { 89293ec6949SJoe Komlodi aspeed_i3c_device_send_disec(s); 89393ec6949SJoe Komlodi } 89493ec6949SJoe Komlodi aspeed_i3c_device_ibi_queue_push(s); 89593ec6949SJoe Komlodi 89693ec6949SJoe Komlodi /* Clear out the intermediate values. */ 89793ec6949SJoe Komlodi s->ibi_data.ibi_queue_status = 0; 89893ec6949SJoe Komlodi s->ibi_data.disec_addr = 0; 89993ec6949SJoe Komlodi s->ibi_data.disec_byte = 0; 90093ec6949SJoe Komlodi s->ibi_data.send_direct_disec = false; 90193ec6949SJoe Komlodi s->ibi_data.notify_ibi_nack = false; 90293ec6949SJoe Komlodi s->ibi_data.ibi_nacked = false; 90393ec6949SJoe Komlodi 90493ec6949SJoe Komlodi return 0; 90593ec6949SJoe Komlodi } 90693ec6949SJoe Komlodi 9073816bedaSJoe Komlodi static uint32_t aspeed_i3c_device_intr_status_r(AspeedI3CDevice *s) 9083816bedaSJoe Komlodi { 9093816bedaSJoe Komlodi /* Only return the status whose corresponding EN bits are set. */ 9103816bedaSJoe Komlodi return s->regs[R_INTR_STATUS] & s->regs[R_INTR_STATUS_EN]; 9113816bedaSJoe Komlodi } 9123816bedaSJoe Komlodi 9133816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_w(AspeedI3CDevice *s, uint32_t val) 9143816bedaSJoe Komlodi { 9153816bedaSJoe Komlodi /* INTR_STATUS[13:5] is w1c, other bits are RO. */ 9163816bedaSJoe Komlodi val &= 0x3fe0; 9173816bedaSJoe Komlodi s->regs[R_INTR_STATUS] &= ~val; 9183816bedaSJoe Komlodi 9193816bedaSJoe Komlodi aspeed_i3c_device_update_irq(s); 9203816bedaSJoe Komlodi } 9213816bedaSJoe Komlodi 9223816bedaSJoe Komlodi static void aspeed_i3c_device_intr_status_en_w(AspeedI3CDevice *s, uint32_t val) 9233816bedaSJoe Komlodi { 9243816bedaSJoe Komlodi s->regs[R_INTR_STATUS_EN] = val; 9253816bedaSJoe Komlodi aspeed_i3c_device_update_irq(s); 9263816bedaSJoe Komlodi } 9273816bedaSJoe Komlodi 9283816bedaSJoe Komlodi static void aspeed_i3c_device_intr_signal_en_w(AspeedI3CDevice *s, uint32_t val) 9293816bedaSJoe Komlodi { 9303816bedaSJoe Komlodi s->regs[R_INTR_SIGNAL_EN] = val; 9313816bedaSJoe Komlodi aspeed_i3c_device_update_irq(s); 9323816bedaSJoe Komlodi } 9333816bedaSJoe Komlodi 9343816bedaSJoe Komlodi static void aspeed_i3c_device_intr_force_w(AspeedI3CDevice *s, uint32_t val) 9353816bedaSJoe Komlodi { 9363816bedaSJoe Komlodi /* INTR_FORCE is WO, just set the corresponding INTR_STATUS bits. */ 9373816bedaSJoe Komlodi s->regs[R_INTR_STATUS] = val; 9383816bedaSJoe Komlodi aspeed_i3c_device_update_irq(s); 9393816bedaSJoe Komlodi } 9403816bedaSJoe Komlodi 9414ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_rx(AspeedI3CDevice *s) 9424ba25376SJoe Komlodi { 9434ba25376SJoe Komlodi if (fifo32_is_empty(&s->rx_queue)) { 9444ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read RX FIFO when empty\n", 9454ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 9464ba25376SJoe Komlodi return 0; 9474ba25376SJoe Komlodi } 9484ba25376SJoe Komlodi 9494ba25376SJoe Komlodi uint32_t val = fifo32_pop(&s->rx_queue); 9504ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, 9514ba25376SJoe Komlodi fifo32_num_used(&s->rx_queue)); 9524ba25376SJoe Komlodi 9534ba25376SJoe Komlodi /* Threshold is 2^RX_BUF_THLD. */ 9544ba25376SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, 9554ba25376SJoe Komlodi RX_BUF_THLD); 9564ba25376SJoe Komlodi threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold); 9574ba25376SJoe Komlodi if (fifo32_num_used(&s->rx_queue) < threshold) { 9584ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 0); 9594ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 9604ba25376SJoe Komlodi } 9614ba25376SJoe Komlodi 9624ba25376SJoe Komlodi trace_aspeed_i3c_device_pop_rx(s->id, val); 9634ba25376SJoe Komlodi return val; 9644ba25376SJoe Komlodi } 9654ba25376SJoe Komlodi 96693ec6949SJoe Komlodi static uint32_t aspeed_i3c_device_ibi_queue_r(AspeedI3CDevice *s) 96793ec6949SJoe Komlodi { 96893ec6949SJoe Komlodi if (fifo32_is_empty(&s->ibi_queue)) { 96993ec6949SJoe Komlodi return 0; 97093ec6949SJoe Komlodi } 97193ec6949SJoe Komlodi 97293ec6949SJoe Komlodi uint32_t val = fifo32_pop(&s->ibi_queue); 97393ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, IBI_BUF_BLR, 97493ec6949SJoe Komlodi fifo32_num_used(&s->ibi_queue)); 97593ec6949SJoe Komlodi /* Threshold is the register value + 1. */ 97693ec6949SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 97793ec6949SJoe Komlodi IBI_STATUS_THLD) + 1; 97893ec6949SJoe Komlodi if (fifo32_num_used(&s->ibi_queue) < threshold) { 97993ec6949SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, IBI_THLD, 0); 98093ec6949SJoe Komlodi aspeed_i3c_device_update_irq(s); 98193ec6949SJoe Komlodi } 98293ec6949SJoe Komlodi return val; 98393ec6949SJoe Komlodi } 98493ec6949SJoe Komlodi 9854ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_resp_queue_port_r(AspeedI3CDevice *s) 9864ba25376SJoe Komlodi { 9874ba25376SJoe Komlodi if (fifo32_is_empty(&s->resp_queue)) { 9884ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to read response FIFO when " 9894ba25376SJoe Komlodi "empty\n", object_get_canonical_path(OBJECT(s))); 9904ba25376SJoe Komlodi return 0; 9914ba25376SJoe Komlodi } 9924ba25376SJoe Komlodi 9934ba25376SJoe Komlodi uint32_t val = fifo32_pop(&s->resp_queue); 9944ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR, 9954ba25376SJoe Komlodi fifo32_num_used(&s->resp_queue)); 9964ba25376SJoe Komlodi 9974ba25376SJoe Komlodi /* Threshold is the register value + 1. */ 9984ba25376SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 9994ba25376SJoe Komlodi RESP_BUF_THLD) + 1; 10004ba25376SJoe Komlodi if (fifo32_num_used(&s->resp_queue) < threshold) { 10014ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 0); 10024ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 10034ba25376SJoe Komlodi } 10044ba25376SJoe Komlodi 10054ba25376SJoe Komlodi return val; 10064ba25376SJoe Komlodi } 10074ba25376SJoe Komlodi 10087d87775fSJoe Komlodi static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset, 10097d87775fSJoe Komlodi unsigned size) 10107d87775fSJoe Komlodi { 10117d87775fSJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque); 10127d87775fSJoe Komlodi uint32_t addr = offset >> 2; 10137d87775fSJoe Komlodi uint64_t value; 10147d87775fSJoe Komlodi 10157d87775fSJoe Komlodi switch (addr) { 101609467a8aSJoe Komlodi /* RAZ */ 10177d87775fSJoe Komlodi case R_COMMAND_QUEUE_PORT: 101809467a8aSJoe Komlodi case R_RESET_CTRL: 101909467a8aSJoe Komlodi case R_INTR_FORCE: 10207d87775fSJoe Komlodi value = 0; 10217d87775fSJoe Komlodi break; 102293ec6949SJoe Komlodi case R_IBI_QUEUE_DATA: 102393ec6949SJoe Komlodi value = aspeed_i3c_device_ibi_queue_r(s); 102493ec6949SJoe Komlodi break; 10253816bedaSJoe Komlodi case R_INTR_STATUS: 10263816bedaSJoe Komlodi value = aspeed_i3c_device_intr_status_r(s); 10273816bedaSJoe Komlodi break; 10284ba25376SJoe Komlodi case R_RX_TX_DATA_PORT: 10294ba25376SJoe Komlodi value = aspeed_i3c_device_pop_rx(s); 10304ba25376SJoe Komlodi break; 10314ba25376SJoe Komlodi case R_RESPONSE_QUEUE_PORT: 10324ba25376SJoe Komlodi value = aspeed_i3c_device_resp_queue_port_r(s); 10334ba25376SJoe Komlodi break; 10347d87775fSJoe Komlodi default: 10357d87775fSJoe Komlodi value = s->regs[addr]; 10367d87775fSJoe Komlodi break; 10377d87775fSJoe Komlodi } 10387d87775fSJoe Komlodi 10397d87775fSJoe Komlodi trace_aspeed_i3c_device_read(s->id, offset, value); 10407d87775fSJoe Komlodi 10417d87775fSJoe Komlodi return value; 10427d87775fSJoe Komlodi } 10437d87775fSJoe Komlodi 10444ba25376SJoe Komlodi static void aspeed_i3c_device_resp_queue_push(AspeedI3CDevice *s, 10454ba25376SJoe Komlodi uint8_t err, uint8_t tid, 10464ba25376SJoe Komlodi uint8_t ccc_type, 10474ba25376SJoe Komlodi uint16_t data_len) 10484ba25376SJoe Komlodi { 10494ba25376SJoe Komlodi uint32_t val = 0; 10504ba25376SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, ERR_STATUS, err); 10514ba25376SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, TID, tid); 10524ba25376SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, CCCT, ccc_type); 10534ba25376SJoe Komlodi val = FIELD_DP32(val, RESPONSE_QUEUE_PORT, DL, data_len); 10544ba25376SJoe Komlodi if (!fifo32_is_full(&s->resp_queue)) { 10554ba25376SJoe Komlodi trace_aspeed_i3c_device_resp_queue_push(s->id, val); 10564ba25376SJoe Komlodi fifo32_push(&s->resp_queue, val); 10574ba25376SJoe Komlodi } 10584ba25376SJoe Komlodi 10594ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, RESP_BUF_BLR, 10604ba25376SJoe Komlodi fifo32_num_used(&s->resp_queue)); 10614ba25376SJoe Komlodi /* Threshold is the register value + 1. */ 10624ba25376SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 10634ba25376SJoe Komlodi RESP_BUF_THLD) + 1; 10644ba25376SJoe Komlodi if (fifo32_num_used(&s->resp_queue) >= threshold) { 10654ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RESP_RDY, 1); 10664ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 10674ba25376SJoe Komlodi } 10684ba25376SJoe Komlodi } 10694ba25376SJoe Komlodi 10704ba25376SJoe Komlodi static void aspeed_i3c_device_push_tx(AspeedI3CDevice *s, uint32_t val) 10714ba25376SJoe Komlodi { 10724ba25376SJoe Komlodi if (fifo32_is_full(&s->tx_queue)) { 10734ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when " 10744ba25376SJoe Komlodi "full\n", object_get_canonical_path(OBJECT(s))); 10754ba25376SJoe Komlodi return; 10764ba25376SJoe Komlodi } 10774ba25376SJoe Komlodi 10784ba25376SJoe Komlodi trace_aspeed_i3c_device_push_tx(s->id, val); 10794ba25376SJoe Komlodi fifo32_push(&s->tx_queue, val); 10804ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 10814ba25376SJoe Komlodi fifo32_num_free(&s->tx_queue)); 10824ba25376SJoe Komlodi 10834ba25376SJoe Komlodi /* Threshold is 2^TX_BUF_THLD. */ 10844ba25376SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, 10854ba25376SJoe Komlodi TX_BUF_THLD); 10864ba25376SJoe Komlodi empty_threshold = 10874ba25376SJoe Komlodi aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold); 10884ba25376SJoe Komlodi if (fifo32_num_free(&s->tx_queue) < empty_threshold) { 10894ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 0); 10904ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 10914ba25376SJoe Komlodi } 10924ba25376SJoe Komlodi } 10934ba25376SJoe Komlodi 10944ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_pop_tx(AspeedI3CDevice *s) 10954ba25376SJoe Komlodi { 10964ba25376SJoe Komlodi if (fifo32_is_empty(&s->tx_queue)) { 10974ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to pop from TX FIFO when " 10984ba25376SJoe Komlodi "empty\n", object_get_canonical_path(OBJECT(s))); 10994ba25376SJoe Komlodi return 0; 11004ba25376SJoe Komlodi } 11014ba25376SJoe Komlodi 11024ba25376SJoe Komlodi uint32_t val = fifo32_pop(&s->tx_queue); 11034ba25376SJoe Komlodi trace_aspeed_i3c_device_pop_tx(s->id, val); 11044ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC, 11054ba25376SJoe Komlodi fifo32_num_free(&s->tx_queue)); 11064ba25376SJoe Komlodi 11074ba25376SJoe Komlodi /* Threshold is 2^TX_BUF_THLD. */ 11084ba25376SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, 11094ba25376SJoe Komlodi TX_BUF_THLD); 11104ba25376SJoe Komlodi empty_threshold = 11114ba25376SJoe Komlodi aspeed_i3c_device_fifo_threshold_from_reg(empty_threshold); 11124ba25376SJoe Komlodi if (fifo32_num_free(&s->tx_queue) >= empty_threshold) { 11134ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, TX_THLD, 1); 11144ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 11154ba25376SJoe Komlodi } 11164ba25376SJoe Komlodi return val; 11174ba25376SJoe Komlodi } 11184ba25376SJoe Komlodi 11194ba25376SJoe Komlodi static void aspeed_i3c_device_push_rx(AspeedI3CDevice *s, uint32_t val) 11204ba25376SJoe Komlodi { 11214ba25376SJoe Komlodi if (fifo32_is_full(&s->rx_queue)) { 11224ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to RX FIFO when " 11234ba25376SJoe Komlodi "full\n", object_get_canonical_path(OBJECT(s))); 11244ba25376SJoe Komlodi return; 11254ba25376SJoe Komlodi } 11264ba25376SJoe Komlodi trace_aspeed_i3c_device_push_rx(s->id, val); 11274ba25376SJoe Komlodi fifo32_push(&s->rx_queue, val); 11284ba25376SJoe Komlodi 11294ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, RX_BUF_BLR, 11304ba25376SJoe Komlodi fifo32_num_used(&s->rx_queue)); 11314ba25376SJoe Komlodi /* Threshold is 2^RX_BUF_THLD. */ 11324ba25376SJoe Komlodi uint8_t threshold = ARRAY_FIELD_EX32(s->regs, DATA_BUFFER_THLD_CTRL, 11334ba25376SJoe Komlodi RX_BUF_THLD); 11344ba25376SJoe Komlodi threshold = aspeed_i3c_device_fifo_threshold_from_reg(threshold); 11354ba25376SJoe Komlodi if (fifo32_num_used(&s->rx_queue) >= threshold) { 11364ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, RX_THLD, 1); 11374ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 11384ba25376SJoe Komlodi } 11394ba25376SJoe Komlodi } 11404ba25376SJoe Komlodi 11414ba25376SJoe Komlodi static void aspeed_i3c_device_short_transfer(AspeedI3CDevice *s, 11424ba25376SJoe Komlodi AspeedI3CTransferCmd cmd, 11434ba25376SJoe Komlodi AspeedI3CShortArg arg) 11444ba25376SJoe Komlodi { 11454ba25376SJoe Komlodi uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; 11464ba25376SJoe Komlodi uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); 11474ba25376SJoe Komlodi bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index); 11484ba25376SJoe Komlodi uint8_t data[4]; /* Max we can send on a short transfer is 4 bytes. */ 11494ba25376SJoe Komlodi uint8_t len = 0; 11504ba25376SJoe Komlodi uint32_t bytes_sent; /* Ignored on short transfers. */ 11514ba25376SJoe Komlodi 11524ba25376SJoe Komlodi /* Can't do reads on a short transfer. */ 11534ba25376SJoe Komlodi if (cmd.rnw) { 11544ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot do a read on a short " 11554ba25376SJoe Komlodi "transfer\n", object_get_canonical_path(OBJECT(s))); 11564ba25376SJoe Komlodi return; 11574ba25376SJoe Komlodi } 11584ba25376SJoe Komlodi 11594ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, addr, /*is_recv=*/false, is_i2c)) { 11604ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; 11614ba25376SJoe Komlodi goto transfer_done; 11624ba25376SJoe Komlodi } 11634ba25376SJoe Komlodi 11644ba25376SJoe Komlodi /* Are we sending a command? */ 11654ba25376SJoe Komlodi if (cmd.cp) { 11664ba25376SJoe Komlodi data[len] = cmd.cmd; 11674ba25376SJoe Komlodi len++; 11684ba25376SJoe Komlodi /* 11694ba25376SJoe Komlodi * byte0 is the defining byte for a command, and is only sent if a 11704ba25376SJoe Komlodi * command is present and if the command has a defining byte present. 11714ba25376SJoe Komlodi * (byte_strb & 0x01) is always treated as set by the controller, and is 11724ba25376SJoe Komlodi * ignored. 11734ba25376SJoe Komlodi */ 11744ba25376SJoe Komlodi if (cmd.dbp) { 11754ba25376SJoe Komlodi data[len] += arg.byte0; 11764ba25376SJoe Komlodi len++; 11774ba25376SJoe Komlodi } 11784ba25376SJoe Komlodi } 11794ba25376SJoe Komlodi 11804ba25376SJoe Komlodi /* Send the bytes passed in the argument. */ 11814ba25376SJoe Komlodi if (arg.byte_strb & 0x02) { 11824ba25376SJoe Komlodi data[len] = arg.byte1; 11834ba25376SJoe Komlodi len++; 11844ba25376SJoe Komlodi } 11854ba25376SJoe Komlodi if (arg.byte_strb & 0x04) { 11864ba25376SJoe Komlodi data[len] = arg.byte2; 11874ba25376SJoe Komlodi len++; 11884ba25376SJoe Komlodi } 11894ba25376SJoe Komlodi 11904ba25376SJoe Komlodi if (aspeed_i3c_device_send(s, data, len, &bytes_sent, is_i2c)) { 11914ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; 11924ba25376SJoe Komlodi } else { 11934ba25376SJoe Komlodi /* Only go to an idle state on a successful transfer. */ 11944ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 11954ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 11964ba25376SJoe Komlodi } 11974ba25376SJoe Komlodi 11984ba25376SJoe Komlodi transfer_done: 11994ba25376SJoe Komlodi if (cmd.toc) { 12004ba25376SJoe Komlodi aspeed_i3c_device_end_transfer(s, is_i2c); 12014ba25376SJoe Komlodi } 12024ba25376SJoe Komlodi if (cmd.roc) { 12034ba25376SJoe Komlodi /* 12044ba25376SJoe Komlodi * ccc_type is always 0 in controller mode, data_len is 0 in short 12054ba25376SJoe Komlodi * transfers. 12064ba25376SJoe Komlodi */ 12074ba25376SJoe Komlodi aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, 12084ba25376SJoe Komlodi /*data_len=*/0); 12094ba25376SJoe Komlodi } 12104ba25376SJoe Komlodi } 12114ba25376SJoe Komlodi 12124ba25376SJoe Komlodi /* Returns number of bytes transmitted. */ 12134ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_tx(AspeedI3CDevice *s, uint16_t num, 12144ba25376SJoe Komlodi bool is_i2c) 12154ba25376SJoe Komlodi { 12164ba25376SJoe Komlodi uint16_t bytes_sent = 0; 12174ba25376SJoe Komlodi union { 12184ba25376SJoe Komlodi uint8_t b[sizeof(uint32_t)]; 12194ba25376SJoe Komlodi uint32_t val; 12204ba25376SJoe Komlodi } val32; 12214ba25376SJoe Komlodi 12224ba25376SJoe Komlodi while (bytes_sent < num) { 12234ba25376SJoe Komlodi val32.val = aspeed_i3c_device_pop_tx(s); 12244ba25376SJoe Komlodi for (uint8_t i = 0; i < sizeof(val32.val); i++) { 12254ba25376SJoe Komlodi if (aspeed_i3c_device_send_byte(s, val32.b[i], is_i2c)) { 12264ba25376SJoe Komlodi return bytes_sent; 12274ba25376SJoe Komlodi } 12284ba25376SJoe Komlodi bytes_sent++; 12294ba25376SJoe Komlodi 12304ba25376SJoe Komlodi /* We're not sending the full 32-bits, break early. */ 12314ba25376SJoe Komlodi if (bytes_sent >= num) { 12324ba25376SJoe Komlodi break; 12334ba25376SJoe Komlodi } 12344ba25376SJoe Komlodi } 12354ba25376SJoe Komlodi } 12364ba25376SJoe Komlodi 12374ba25376SJoe Komlodi return bytes_sent; 12384ba25376SJoe Komlodi } 12394ba25376SJoe Komlodi 12404ba25376SJoe Komlodi /* Returns number of bytes received. */ 12414ba25376SJoe Komlodi static uint16_t aspeed_i3c_device_rx(AspeedI3CDevice *s, uint16_t num, 12424ba25376SJoe Komlodi bool is_i2c) 12434ba25376SJoe Komlodi { 12444ba25376SJoe Komlodi /* 12454ba25376SJoe Komlodi * Allocate a temporary buffer to read data from the target. 12464ba25376SJoe Komlodi * Zero it and word-align it as well in case we're reading unaligned data. 12474ba25376SJoe Komlodi */ 12484ba25376SJoe Komlodi g_autofree uint8_t *data = g_new0(uint8_t, num + (num & 0x03)); 12494ba25376SJoe Komlodi uint32_t *data32 = (uint32_t *)data; 12504ba25376SJoe Komlodi /* 12514ba25376SJoe Komlodi * 32-bits since the I3C API wants a 32-bit number, even though the 12524ba25376SJoe Komlodi * controller can only do 16-bit transfers. 12534ba25376SJoe Komlodi */ 12544ba25376SJoe Komlodi uint32_t num_read = 0; 12554ba25376SJoe Komlodi 12564ba25376SJoe Komlodi /* Can NACK if the target receives an unsupported CCC. */ 12574ba25376SJoe Komlodi if (aspeed_i3c_device_recv_data(s, is_i2c, data, num, &num_read)) { 12584ba25376SJoe Komlodi return 0; 12594ba25376SJoe Komlodi } 12604ba25376SJoe Komlodi 12614ba25376SJoe Komlodi for (uint16_t i = 0; i < num_read / 4; i++) { 12624ba25376SJoe Komlodi aspeed_i3c_device_push_rx(s, *data32); 12634ba25376SJoe Komlodi data32++; 12644ba25376SJoe Komlodi } 12654ba25376SJoe Komlodi /* 12664ba25376SJoe Komlodi * If we're pushing data that isn't 32-bit aligned, push what's left. 12674ba25376SJoe Komlodi * It's software's responsibility to know what bits are valid in the partial 12684ba25376SJoe Komlodi * data. 12694ba25376SJoe Komlodi */ 12704ba25376SJoe Komlodi if (num_read & 0x03) { 12714ba25376SJoe Komlodi aspeed_i3c_device_push_rx(s, *data32); 12724ba25376SJoe Komlodi } 12734ba25376SJoe Komlodi 12744ba25376SJoe Komlodi return num_read; 12754ba25376SJoe Komlodi } 12764ba25376SJoe Komlodi 12774ba25376SJoe Komlodi static int aspeed_i3c_device_transfer_ccc(AspeedI3CDevice *s, 12784ba25376SJoe Komlodi AspeedI3CTransferCmd cmd, 12794ba25376SJoe Komlodi AspeedI3CTransferArg arg) 12804ba25376SJoe Komlodi { 12814ba25376SJoe Komlodi /* CCC start is always a write. CCCs cannot be done on I2C devices. */ 12824ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, 12834ba25376SJoe Komlodi /*is_i2c=*/false)) { 12844ba25376SJoe Komlodi return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; 12854ba25376SJoe Komlodi } 12864ba25376SJoe Komlodi trace_aspeed_i3c_device_transfer_ccc(s->id, cmd.cmd); 12874ba25376SJoe Komlodi if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) { 12884ba25376SJoe Komlodi return ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; 12894ba25376SJoe Komlodi } 12904ba25376SJoe Komlodi 12914ba25376SJoe Komlodi /* On a direct CCC, we do a restart and then send the target's address. */ 12924ba25376SJoe Komlodi if (CCC_IS_DIRECT(cmd.cmd)) { 12934ba25376SJoe Komlodi bool is_recv = cmd.rnw; 12944ba25376SJoe Komlodi uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); 12954ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, addr, is_recv, /*is_i2c=*/false)) { 12964ba25376SJoe Komlodi return ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; 12974ba25376SJoe Komlodi } 12984ba25376SJoe Komlodi } 12994ba25376SJoe Komlodi 13004ba25376SJoe Komlodi return ASPEED_I3C_RESP_QUEUE_ERR_NONE; 13014ba25376SJoe Komlodi } 13024ba25376SJoe Komlodi 13034ba25376SJoe Komlodi static void aspeed_i3c_device_transfer(AspeedI3CDevice *s, 13044ba25376SJoe Komlodi AspeedI3CTransferCmd cmd, 13054ba25376SJoe Komlodi AspeedI3CTransferArg arg) 13064ba25376SJoe Komlodi { 13074ba25376SJoe Komlodi bool is_recv = cmd.rnw; 13084ba25376SJoe Komlodi uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; 13094ba25376SJoe Komlodi uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index); 13104ba25376SJoe Komlodi bool is_i2c = aspeed_i3c_device_target_is_i2c(s, cmd.dev_index); 13114ba25376SJoe Komlodi uint16_t bytes_transferred = 0; 13124ba25376SJoe Komlodi 13134ba25376SJoe Komlodi if (cmd.cp) { 13144ba25376SJoe Komlodi /* We're sending a CCC. */ 13154ba25376SJoe Komlodi err = aspeed_i3c_device_transfer_ccc(s, cmd, arg); 13164ba25376SJoe Komlodi if (err != ASPEED_I3C_RESP_QUEUE_ERR_NONE) { 13174ba25376SJoe Komlodi goto transfer_done; 13184ba25376SJoe Komlodi } 13194ba25376SJoe Komlodi } else { 13204ba25376SJoe Komlodi if (ARRAY_FIELD_EX32(s->regs, DEVICE_CTRL, I3C_BROADCAST_ADDR_INC) && 13214ba25376SJoe Komlodi is_i2c == false) { 13224ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, 13234ba25376SJoe Komlodi /*is_recv=*/false, is_i2c)) { 13244ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; 13254ba25376SJoe Komlodi goto transfer_done; 13264ba25376SJoe Komlodi } 13274ba25376SJoe Komlodi } 13284ba25376SJoe Komlodi /* Otherwise we're doing a private transfer. */ 13294ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, addr, is_recv, is_i2c)) { 13304ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_I2C_NACK; 13314ba25376SJoe Komlodi goto transfer_done; 13324ba25376SJoe Komlodi } 13334ba25376SJoe Komlodi } 13344ba25376SJoe Komlodi 13354ba25376SJoe Komlodi if (is_recv) { 13364ba25376SJoe Komlodi bytes_transferred = aspeed_i3c_device_rx(s, arg.data_len, is_i2c); 13374ba25376SJoe Komlodi } else { 13384ba25376SJoe Komlodi bytes_transferred = aspeed_i3c_device_tx(s, arg.data_len, is_i2c); 13394ba25376SJoe Komlodi } 13404ba25376SJoe Komlodi 13414ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 13424ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 13434ba25376SJoe Komlodi 13444ba25376SJoe Komlodi transfer_done: 13454ba25376SJoe Komlodi if (cmd.toc) { 13464ba25376SJoe Komlodi aspeed_i3c_device_end_transfer(s, is_i2c); 13474ba25376SJoe Komlodi } 13484ba25376SJoe Komlodi if (cmd.roc) { 13494ba25376SJoe Komlodi /* 13504ba25376SJoe Komlodi * data_len is the number of bytes that still need to be TX'd, or the 13514ba25376SJoe Komlodi * number of bytes RX'd. 13524ba25376SJoe Komlodi */ 13534ba25376SJoe Komlodi uint16_t data_len = is_recv ? bytes_transferred : arg.data_len - 13544ba25376SJoe Komlodi bytes_transferred; 13554ba25376SJoe Komlodi /* CCCT is always 0 in controller mode. */ 13564ba25376SJoe Komlodi aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, 13574ba25376SJoe Komlodi data_len); 13584ba25376SJoe Komlodi } 13594ba25376SJoe Komlodi 13604ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 13614ba25376SJoe Komlodi } 13624ba25376SJoe Komlodi 13634ba25376SJoe Komlodi static void aspeed_i3c_device_transfer_cmd(AspeedI3CDevice *s, 13644ba25376SJoe Komlodi AspeedI3CTransferCmd cmd, 13654ba25376SJoe Komlodi AspeedI3CCmdQueueData arg) 13664ba25376SJoe Komlodi { 13674ba25376SJoe Komlodi uint8_t arg_attr = FIELD_EX32(arg.word, COMMAND_QUEUE_PORT, CMD_ATTR); 13684ba25376SJoe Komlodi 13694ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CMD_TID, cmd.tid); 13704ba25376SJoe Komlodi 13714ba25376SJoe Komlodi /* User is trying to do HDR transfers, see if we can do them. */ 13724ba25376SJoe Komlodi if (cmd.speed == 0x06 && !aspeed_i3c_device_has_hdr_ddr(s)) { 13734ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR DDR is not supported\n", 13744ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 13754ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 13764ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 13774ba25376SJoe Komlodi return; 13784ba25376SJoe Komlodi } 13794ba25376SJoe Komlodi if (cmd.speed == 0x05 && !aspeed_i3c_device_has_hdr_ts(s)) { 13804ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: HDR TS is not supported\n", 13814ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 13824ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 13834ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 13844ba25376SJoe Komlodi return; 13854ba25376SJoe Komlodi } 13864ba25376SJoe Komlodi 13874ba25376SJoe Komlodi if (arg_attr == ASPEED_I3C_CMD_ATTR_TRANSFER_ARG) { 13884ba25376SJoe Komlodi aspeed_i3c_device_transfer(s, cmd, arg.transfer_arg); 13894ba25376SJoe Komlodi } else if (arg_attr == ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG) { 13904ba25376SJoe Komlodi aspeed_i3c_device_short_transfer(s, cmd, arg.short_arg); 13914ba25376SJoe Komlodi } else { 13924ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown command queue cmd_attr 0x%x" 13934ba25376SJoe Komlodi "\n", object_get_canonical_path(OBJECT(s)), arg_attr); 13944ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 13954ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_HALT); 13964ba25376SJoe Komlodi } 13974ba25376SJoe Komlodi } 13984ba25376SJoe Komlodi 13994ba25376SJoe Komlodi static void aspeed_i3c_device_update_char_table(AspeedI3CDevice *s, 14004ba25376SJoe Komlodi uint8_t offset, uint64_t pid, 14014ba25376SJoe Komlodi uint8_t bcr, uint8_t dcr, 14024ba25376SJoe Komlodi uint8_t addr) 14034ba25376SJoe Komlodi { 14044ba25376SJoe Komlodi if (offset > ASPEED_I3C_NR_DEVICES) { 14054ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Device char table offset %d out of " 14064ba25376SJoe Komlodi "bounds\n", object_get_canonical_path(OBJECT(s)), offset); 14074ba25376SJoe Komlodi /* If we're out of bounds, do nothing. */ 14084ba25376SJoe Komlodi return; 14094ba25376SJoe Komlodi } 14104ba25376SJoe Komlodi 14114ba25376SJoe Komlodi /* Each char table index is 128 bits apart. */ 14124ba25376SJoe Komlodi uint16_t dev_index = R_DEVICE_CHARACTERISTIC_TABLE_LOC1 + offset * 14134ba25376SJoe Komlodi sizeof(uint32_t); 14144ba25376SJoe Komlodi s->regs[dev_index] = pid & 0xffffffff; 14154ba25376SJoe Komlodi pid >>= 32; 14164ba25376SJoe Komlodi s->regs[dev_index + 1] = FIELD_DP32(s->regs[dev_index + 1], 14174ba25376SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC2, 14184ba25376SJoe Komlodi MSB_PID, pid); 14194ba25376SJoe Komlodi s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2], 14204ba25376SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC3, DCR, 14214ba25376SJoe Komlodi dcr); 14224ba25376SJoe Komlodi s->regs[dev_index + 2] = FIELD_DP32(s->regs[dev_index + 2], 14234ba25376SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC3, BCR, 14244ba25376SJoe Komlodi bcr); 14254ba25376SJoe Komlodi s->regs[dev_index + 3] = FIELD_DP32(s->regs[dev_index + 3], 14264ba25376SJoe Komlodi DEVICE_CHARACTERISTIC_TABLE_LOC4, 14274ba25376SJoe Komlodi DEV_DYNAMIC_ADDR, addr); 14284ba25376SJoe Komlodi 14294ba25376SJoe Komlodi /* Increment PRESENT_DEV_CHAR_TABLE_INDEX. */ 14304ba25376SJoe Komlodi uint8_t idx = ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER, 14314ba25376SJoe Komlodi PRESENT_DEV_CHAR_TABLE_INDEX); 14324ba25376SJoe Komlodi /* Increment and rollover. */ 14334ba25376SJoe Komlodi idx++; 14344ba25376SJoe Komlodi if (idx >= ARRAY_FIELD_EX32(s->regs, DEV_CHAR_TABLE_POINTER, 14354ba25376SJoe Komlodi DEV_CHAR_TABLE_DEPTH) / 4) { 14364ba25376SJoe Komlodi idx = 0; 14374ba25376SJoe Komlodi } 14384ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, 14394ba25376SJoe Komlodi PRESENT_DEV_CHAR_TABLE_INDEX, idx); 14404ba25376SJoe Komlodi } 14414ba25376SJoe Komlodi 14424ba25376SJoe Komlodi static void aspeed_i3c_device_addr_assign_cmd(AspeedI3CDevice *s, 14434ba25376SJoe Komlodi AspeedI3CAddrAssignCmd cmd) 14444ba25376SJoe Komlodi { 14454ba25376SJoe Komlodi uint8_t i = 0; 14464ba25376SJoe Komlodi uint8_t err = ASPEED_I3C_RESP_QUEUE_ERR_NONE; 14474ba25376SJoe Komlodi 14484ba25376SJoe Komlodi if (!aspeed_i3c_device_has_entdaa(s)) { 14494ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: ENTDAA is not supported\n", 14504ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 14514ba25376SJoe Komlodi return; 14524ba25376SJoe Komlodi } 14534ba25376SJoe Komlodi 14544ba25376SJoe Komlodi /* Tell everyone to ENTDAA. If these error, no one is on the bus. */ 14554ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, 14564ba25376SJoe Komlodi /*is_i2c=*/false)) { 14574ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; 14584ba25376SJoe Komlodi goto transfer_done; 14594ba25376SJoe Komlodi } 14604ba25376SJoe Komlodi if (aspeed_i3c_device_send_byte(s, cmd.cmd, /*is_i2c=*/false)) { 14614ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; 14624ba25376SJoe Komlodi goto transfer_done; 14634ba25376SJoe Komlodi } 14644ba25376SJoe Komlodi 14654ba25376SJoe Komlodi /* Go through each device in the table and assign it an address. */ 14664ba25376SJoe Komlodi for (i = 0; i < cmd.dev_count; i++) { 14674ba25376SJoe Komlodi uint8_t addr = aspeed_i3c_device_target_addr(s, cmd.dev_index + i); 14684ba25376SJoe Komlodi union { 14694ba25376SJoe Komlodi uint64_t pid:48; 14704ba25376SJoe Komlodi uint8_t bcr; 14714ba25376SJoe Komlodi uint8_t dcr; 14724ba25376SJoe Komlodi uint32_t w[2]; 14734ba25376SJoe Komlodi uint8_t b[8]; 14744ba25376SJoe Komlodi } target_info; 14754ba25376SJoe Komlodi 14764ba25376SJoe Komlodi /* If this fails, there was no one left to ENTDAA. */ 14774ba25376SJoe Komlodi if (aspeed_i3c_device_send_start(s, I3C_BROADCAST, /*is_recv=*/false, 14784ba25376SJoe Komlodi /*is_i2c=*/false)) { 14794ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_BROADCAST_NACK; 14804ba25376SJoe Komlodi break; 14814ba25376SJoe Komlodi } 14824ba25376SJoe Komlodi 14834ba25376SJoe Komlodi /* 14844ba25376SJoe Komlodi * In ENTDAA, we read 8 bytes from the target, which will be the 14854ba25376SJoe Komlodi * target's PID, BCR, and DCR. After that, we send it the dynamic 14864ba25376SJoe Komlodi * address. 14874ba25376SJoe Komlodi * Don't bother checking the number of bytes received, it must send 8 14884ba25376SJoe Komlodi * bytes during ENTDAA. 14894ba25376SJoe Komlodi */ 14904ba25376SJoe Komlodi uint32_t num_read; 14914ba25376SJoe Komlodi if (aspeed_i3c_device_recv_data(s, /*is_i2c=*/false, target_info.b, 14924ba25376SJoe Komlodi I3C_ENTDAA_SIZE, &num_read)) { 14934ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed ENTDAA CCC\n", 14944ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 14954ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK; 14964ba25376SJoe Komlodi goto transfer_done; 14974ba25376SJoe Komlodi } 14984ba25376SJoe Komlodi if (aspeed_i3c_device_send_byte(s, addr, /*is_i2c=*/false)) { 14994ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Target NACKed addr 0x%.2x " 15004ba25376SJoe Komlodi "during ENTDAA\n", 15014ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s)), addr); 15024ba25376SJoe Komlodi err = ASPEED_I3C_RESP_QUEUE_ERR_DAA_NACK; 15034ba25376SJoe Komlodi break; 15044ba25376SJoe Komlodi } 15054ba25376SJoe Komlodi aspeed_i3c_device_update_char_table(s, cmd.dev_index + i, 15064ba25376SJoe Komlodi target_info.pid, target_info.bcr, 15074ba25376SJoe Komlodi target_info.dcr, addr); 15084ba25376SJoe Komlodi 15094ba25376SJoe Komlodi /* Push the PID, BCR, and DCR to the RX queue. */ 15104ba25376SJoe Komlodi aspeed_i3c_device_push_rx(s, target_info.w[0]); 15114ba25376SJoe Komlodi aspeed_i3c_device_push_rx(s, target_info.w[1]); 15124ba25376SJoe Komlodi } 15134ba25376SJoe Komlodi 15144ba25376SJoe Komlodi transfer_done: 15154ba25376SJoe Komlodi /* Do we send a STOP? */ 15164ba25376SJoe Komlodi if (cmd.toc) { 15174ba25376SJoe Komlodi aspeed_i3c_device_end_transfer(s, /*is_i2c=*/false); 15184ba25376SJoe Komlodi } 15194ba25376SJoe Komlodi /* 15204ba25376SJoe Komlodi * For addr assign commands, the length field is the number of devices 15214ba25376SJoe Komlodi * left to assign. CCCT is always 0 in controller mode. 15224ba25376SJoe Komlodi */ 15234ba25376SJoe Komlodi if (cmd.roc) { 15244ba25376SJoe Komlodi aspeed_i3c_device_resp_queue_push(s, err, cmd.tid, /*ccc_type=*/0, 15254ba25376SJoe Komlodi cmd.dev_count - i); 15264ba25376SJoe Komlodi } 15274ba25376SJoe Komlodi } 15284ba25376SJoe Komlodi 15294ba25376SJoe Komlodi static uint32_t aspeed_i3c_device_cmd_queue_pop(AspeedI3CDevice *s) 15304ba25376SJoe Komlodi { 15314ba25376SJoe Komlodi if (fifo32_is_empty(&s->cmd_queue)) { 15324ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to dequeue command queue " 15334ba25376SJoe Komlodi "when it was empty\n", 15344ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 15354ba25376SJoe Komlodi return 0; 15364ba25376SJoe Komlodi } 15374ba25376SJoe Komlodi uint32_t val = fifo32_pop(&s->cmd_queue); 15384ba25376SJoe Komlodi 15394ba25376SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 15404ba25376SJoe Komlodi CMD_BUF_EMPTY_THLD); 15414ba25376SJoe Komlodi uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs, 15424ba25376SJoe Komlodi QUEUE_STATUS_LEVEL, 15434ba25376SJoe Komlodi CMD_QUEUE_EMPTY_LOC); 15444ba25376SJoe Komlodi cmd_queue_empty_loc++; 15454ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, 15464ba25376SJoe Komlodi cmd_queue_empty_loc); 15474ba25376SJoe Komlodi if (cmd_queue_empty_loc >= empty_threshold) { 15484ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 1); 15494ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 15504ba25376SJoe Komlodi } 15514ba25376SJoe Komlodi 15524ba25376SJoe Komlodi return val; 15534ba25376SJoe Komlodi } 15544ba25376SJoe Komlodi 15554ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_execute(AspeedI3CDevice *s) 15564ba25376SJoe Komlodi { 15574ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, 15584ba25376SJoe Komlodi ASPEED_I3C_TRANSFER_STATE_IDLE); 15594ba25376SJoe Komlodi if (!aspeed_i3c_device_can_transmit(s)) { 15604ba25376SJoe Komlodi return; 15614ba25376SJoe Komlodi } 15624ba25376SJoe Komlodi 15634ba25376SJoe Komlodi /* 15644ba25376SJoe Komlodi * We only start executing when a command is passed into the FIFO. 15654ba25376SJoe Komlodi * We expect there to be a multiple of 2 items in the queue. The first item 15664ba25376SJoe Komlodi * should be an argument to a command, and the command should be the second 15674ba25376SJoe Komlodi * item. 15684ba25376SJoe Komlodi */ 15694ba25376SJoe Komlodi if (fifo32_num_used(&s->cmd_queue) & 1) { 15704ba25376SJoe Komlodi return; 15714ba25376SJoe Komlodi } 15724ba25376SJoe Komlodi 15734ba25376SJoe Komlodi while (!fifo32_is_empty(&s->cmd_queue)) { 15744ba25376SJoe Komlodi AspeedI3CCmdQueueData arg; 15754ba25376SJoe Komlodi arg.word = aspeed_i3c_device_cmd_queue_pop(s); 15764ba25376SJoe Komlodi AspeedI3CCmdQueueData cmd; 15774ba25376SJoe Komlodi cmd.word = aspeed_i3c_device_cmd_queue_pop(s); 15784ba25376SJoe Komlodi trace_aspeed_i3c_device_cmd_queue_execute(s->id, cmd.word, arg.word); 15794ba25376SJoe Komlodi 15804ba25376SJoe Komlodi uint8_t cmd_attr = FIELD_EX32(cmd.word, COMMAND_QUEUE_PORT, CMD_ATTR); 15814ba25376SJoe Komlodi switch (cmd_attr) { 15824ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD: 15834ba25376SJoe Komlodi aspeed_i3c_device_transfer_cmd(s, cmd.transfer_cmd, arg); 15844ba25376SJoe Komlodi break; 15854ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD: 15864ba25376SJoe Komlodi /* Arg is discarded for addr assign commands. */ 15874ba25376SJoe Komlodi aspeed_i3c_device_addr_assign_cmd(s, cmd.addr_assign_cmd); 15884ba25376SJoe Komlodi break; 15894ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG: 15904ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG: 15914ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received argument" 15924ba25376SJoe Komlodi " packet when it expected a command packet\n", 15934ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s))); 15944ba25376SJoe Komlodi break; 15954ba25376SJoe Komlodi default: 15964ba25376SJoe Komlodi /* 15974ba25376SJoe Komlodi * The caller's check before queueing an item should prevent this 15984ba25376SJoe Komlodi * from happening. 15994ba25376SJoe Komlodi */ 16004ba25376SJoe Komlodi g_assert_not_reached(); 16014ba25376SJoe Komlodi break; 16024ba25376SJoe Komlodi } 16034ba25376SJoe Komlodi } 16044ba25376SJoe Komlodi } 16054ba25376SJoe Komlodi 16064ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_push(AspeedI3CDevice *s, uint32_t val) 16074ba25376SJoe Komlodi { 16084ba25376SJoe Komlodi if (fifo32_is_full(&s->cmd_queue)) { 16094ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet when " 16104ba25376SJoe Komlodi "already full\n", object_get_canonical_path(OBJECT(s))); 16114ba25376SJoe Komlodi return; 16124ba25376SJoe Komlodi } 16134ba25376SJoe Komlodi trace_aspeed_i3c_device_cmd_queue_push(s->id, val); 16144ba25376SJoe Komlodi fifo32_push(&s->cmd_queue, val); 16154ba25376SJoe Komlodi 16164ba25376SJoe Komlodi uint8_t empty_threshold = ARRAY_FIELD_EX32(s->regs, QUEUE_THLD_CTRL, 16174ba25376SJoe Komlodi CMD_BUF_EMPTY_THLD); 16184ba25376SJoe Komlodi uint8_t cmd_queue_empty_loc = ARRAY_FIELD_EX32(s->regs, 16194ba25376SJoe Komlodi QUEUE_STATUS_LEVEL, 16204ba25376SJoe Komlodi CMD_QUEUE_EMPTY_LOC); 16214ba25376SJoe Komlodi if (cmd_queue_empty_loc) { 16224ba25376SJoe Komlodi cmd_queue_empty_loc--; 16234ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC, 16244ba25376SJoe Komlodi cmd_queue_empty_loc); 16254ba25376SJoe Komlodi } 16264ba25376SJoe Komlodi if (cmd_queue_empty_loc < empty_threshold) { 16274ba25376SJoe Komlodi ARRAY_FIELD_DP32(s->regs, INTR_STATUS, CMD_QUEUE_RDY, 0); 16284ba25376SJoe Komlodi aspeed_i3c_device_update_irq(s); 16294ba25376SJoe Komlodi } 16304ba25376SJoe Komlodi } 16314ba25376SJoe Komlodi 16324ba25376SJoe Komlodi static void aspeed_i3c_device_cmd_queue_port_w(AspeedI3CDevice *s, uint32_t val) 16334ba25376SJoe Komlodi { 16344ba25376SJoe Komlodi uint8_t cmd_attr = FIELD_EX32(val, COMMAND_QUEUE_PORT, CMD_ATTR); 16354ba25376SJoe Komlodi 16364ba25376SJoe Komlodi switch (cmd_attr) { 16374ba25376SJoe Komlodi /* If a command is received we can start executing it. */ 16384ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_TRANSFER_CMD: 16394ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_ADDR_ASSIGN_CMD: 16404ba25376SJoe Komlodi aspeed_i3c_device_cmd_queue_push(s, val); 16414ba25376SJoe Komlodi aspeed_i3c_device_cmd_queue_execute(s); 16424ba25376SJoe Komlodi break; 16434ba25376SJoe Komlodi /* If we get an argument just push it. */ 16444ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_TRANSFER_ARG: 16454ba25376SJoe Komlodi case ASPEED_I3C_CMD_ATTR_SHORT_DATA_ARG: 16464ba25376SJoe Komlodi aspeed_i3c_device_cmd_queue_push(s, val); 16474ba25376SJoe Komlodi break; 16484ba25376SJoe Komlodi default: 16494ba25376SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Command queue received packet with " 16504ba25376SJoe Komlodi "unknown cmd attr 0x%x\n", 16514ba25376SJoe Komlodi object_get_canonical_path(OBJECT(s)), cmd_attr); 16524ba25376SJoe Komlodi break; 16534ba25376SJoe Komlodi } 16544ba25376SJoe Komlodi } 16554ba25376SJoe Komlodi 16567d87775fSJoe Komlodi static void aspeed_i3c_device_write(void *opaque, hwaddr offset, 16577d87775fSJoe Komlodi uint64_t value, unsigned size) 16587d87775fSJoe Komlodi { 16597d87775fSJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque); 16607d87775fSJoe Komlodi uint32_t addr = offset >> 2; 166197fb7498SJoe Komlodi uint32_t val32 = (uint32_t)value; 16627d87775fSJoe Komlodi 16637d87775fSJoe Komlodi trace_aspeed_i3c_device_write(s->id, offset, value); 16647d87775fSJoe Komlodi 166597fb7498SJoe Komlodi val32 &= ~ast2600_i3c_device_ro[addr]; 16667d87775fSJoe Komlodi switch (addr) { 16677d87775fSJoe Komlodi case R_HW_CAPABILITY: 16687d87775fSJoe Komlodi case R_RESPONSE_QUEUE_PORT: 16697d87775fSJoe Komlodi case R_IBI_QUEUE_DATA: 16707d87775fSJoe Komlodi case R_QUEUE_STATUS_LEVEL: 16717d87775fSJoe Komlodi case R_PRESENT_STATE: 16727d87775fSJoe Komlodi case R_CCC_DEVICE_STATUS: 16737d87775fSJoe Komlodi case R_DEVICE_ADDR_TABLE_POINTER: 16747d87775fSJoe Komlodi case R_VENDOR_SPECIFIC_REG_POINTER: 16757d87775fSJoe Komlodi case R_SLV_CHAR_CTRL: 16767d87775fSJoe Komlodi case R_SLV_MAX_LEN: 16777d87775fSJoe Komlodi case R_MAX_READ_TURNAROUND: 16787d87775fSJoe Komlodi case R_I3C_VER_ID: 16797d87775fSJoe Komlodi case R_I3C_VER_TYPE: 16807d87775fSJoe Komlodi case R_EXTENDED_CAPABILITY: 16817d87775fSJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, 16827d87775fSJoe Komlodi "%s: write to readonly register[0x%02" HWADDR_PRIx 16837d87775fSJoe Komlodi "] = 0x%08" PRIx64 "\n", 16847d87775fSJoe Komlodi __func__, offset, value); 16857d87775fSJoe Komlodi break; 1686*eee51e8fSJoe Komlodi case R_DEVICE_CTRL: 1687*eee51e8fSJoe Komlodi aspeed_i3c_device_ctrl_w(s, val32); 1688*eee51e8fSJoe Komlodi break; 16897d87775fSJoe Komlodi case R_RX_TX_DATA_PORT: 16904ba25376SJoe Komlodi aspeed_i3c_device_push_tx(s, val32); 16914ba25376SJoe Komlodi break; 16924ba25376SJoe Komlodi case R_COMMAND_QUEUE_PORT: 16934ba25376SJoe Komlodi aspeed_i3c_device_cmd_queue_port_w(s, val32); 16947d87775fSJoe Komlodi break; 16957d87775fSJoe Komlodi case R_RESET_CTRL: 16967d87775fSJoe Komlodi break; 16973816bedaSJoe Komlodi case R_INTR_STATUS: 16983816bedaSJoe Komlodi aspeed_i3c_device_intr_status_w(s, val32); 16993816bedaSJoe Komlodi break; 17003816bedaSJoe Komlodi case R_INTR_STATUS_EN: 17013816bedaSJoe Komlodi aspeed_i3c_device_intr_status_en_w(s, val32); 17023816bedaSJoe Komlodi break; 17033816bedaSJoe Komlodi case R_INTR_SIGNAL_EN: 17043816bedaSJoe Komlodi aspeed_i3c_device_intr_signal_en_w(s, val32); 17053816bedaSJoe Komlodi break; 17063816bedaSJoe Komlodi case R_INTR_FORCE: 17073816bedaSJoe Komlodi aspeed_i3c_device_intr_force_w(s, val32); 17083816bedaSJoe Komlodi break; 17097d87775fSJoe Komlodi default: 171097fb7498SJoe Komlodi s->regs[addr] = val32; 17117d87775fSJoe Komlodi break; 17127d87775fSJoe Komlodi } 17137d87775fSJoe Komlodi } 17147d87775fSJoe Komlodi 17157d87775fSJoe Komlodi static const VMStateDescription aspeed_i3c_device_vmstate = { 17167d87775fSJoe Komlodi .name = TYPE_ASPEED_I3C, 17177d87775fSJoe Komlodi .version_id = 1, 17187d87775fSJoe Komlodi .minimum_version_id = 1, 17197d87775fSJoe Komlodi .fields = (const VMStateField[]){ 17207d87775fSJoe Komlodi VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS), 17217d87775fSJoe Komlodi VMSTATE_END_OF_LIST(), 17227d87775fSJoe Komlodi } 17237d87775fSJoe Komlodi }; 17247d87775fSJoe Komlodi 17257d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_device_ops = { 17267d87775fSJoe Komlodi .read = aspeed_i3c_device_read, 17277d87775fSJoe Komlodi .write = aspeed_i3c_device_write, 17287d87775fSJoe Komlodi .endianness = DEVICE_LITTLE_ENDIAN, 17297d87775fSJoe Komlodi }; 17307d87775fSJoe Komlodi 17317d87775fSJoe Komlodi static void aspeed_i3c_device_reset(DeviceState *dev) 17327d87775fSJoe Komlodi { 17337d87775fSJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev); 17347d87775fSJoe Komlodi 17357d87775fSJoe Komlodi memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs)); 17367d87775fSJoe Komlodi } 17377d87775fSJoe Komlodi 17387d87775fSJoe Komlodi static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp) 17397d87775fSJoe Komlodi { 17407d87775fSJoe Komlodi AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev); 17417d87775fSJoe Komlodi g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I3C_DEVICE ".%d", 17427d87775fSJoe Komlodi s->id); 17437d87775fSJoe Komlodi 17447d87775fSJoe Komlodi sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 17457d87775fSJoe Komlodi 17467d87775fSJoe Komlodi memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops, 17477d87775fSJoe Komlodi s, name, ASPEED_I3C_DEVICE_NR_REGS << 2); 17484ba25376SJoe Komlodi 17494ba25376SJoe Komlodi fifo32_create(&s->cmd_queue, ASPEED_I3C_CMD_QUEUE_CAPACITY); 17504ba25376SJoe Komlodi fifo32_create(&s->resp_queue, ASPEED_I3C_RESP_QUEUE_CAPACITY); 17514ba25376SJoe Komlodi fifo32_create(&s->tx_queue, ASPEED_I3C_TX_QUEUE_CAPACITY); 17524ba25376SJoe Komlodi fifo32_create(&s->rx_queue, ASPEED_I3C_RX_QUEUE_CAPACITY); 175393ec6949SJoe Komlodi fifo32_create(&s->ibi_queue, ASPEED_I3C_IBI_QUEUE_CAPACITY); 175493ec6949SJoe Komlodi /* Arbitrarily large enough to not be an issue. */ 175593ec6949SJoe Komlodi fifo8_create(&s->ibi_data.ibi_intermediate_queue, 175693ec6949SJoe Komlodi ASPEED_I3C_IBI_QUEUE_CAPACITY * 8); 17574ba25376SJoe Komlodi 17584ba25376SJoe Komlodi s->bus = i3c_init_bus(DEVICE(s), name); 175993ec6949SJoe Komlodi I3CBusClass *bc = I3C_BUS_GET_CLASS(s->bus); 176093ec6949SJoe Komlodi bc->ibi_handle = aspeed_i3c_device_ibi_handle; 176193ec6949SJoe Komlodi bc->ibi_recv = aspeed_i3c_device_ibi_recv; 176293ec6949SJoe Komlodi bc->ibi_finish = aspeed_i3c_device_ibi_finish; 17637d87775fSJoe Komlodi } 17647d87775fSJoe Komlodi 17657d87775fSJoe Komlodi static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size) 17667d87775fSJoe Komlodi { 17677d87775fSJoe Komlodi AspeedI3CState *s = ASPEED_I3C(opaque); 17687d87775fSJoe Komlodi uint64_t val = 0; 17697d87775fSJoe Komlodi 17707d87775fSJoe Komlodi val = s->regs[addr >> 2]; 17717d87775fSJoe Komlodi 17727d87775fSJoe Komlodi trace_aspeed_i3c_read(addr, val); 17737d87775fSJoe Komlodi 17747d87775fSJoe Komlodi return val; 17757d87775fSJoe Komlodi } 17767d87775fSJoe Komlodi 17777d87775fSJoe Komlodi static void aspeed_i3c_write(void *opaque, 17787d87775fSJoe Komlodi hwaddr addr, 17797d87775fSJoe Komlodi uint64_t data, 17807d87775fSJoe Komlodi unsigned int size) 17817d87775fSJoe Komlodi { 17827d87775fSJoe Komlodi AspeedI3CState *s = ASPEED_I3C(opaque); 17837d87775fSJoe Komlodi 17847d87775fSJoe Komlodi trace_aspeed_i3c_write(addr, data); 17857d87775fSJoe Komlodi 17867d87775fSJoe Komlodi addr >>= 2; 17877d87775fSJoe Komlodi 1788a9d3f922SJoe Komlodi data &= ~ast2600_i3c_controller_ro[addr]; 17897d87775fSJoe Komlodi /* I3C controller register */ 17907d87775fSJoe Komlodi switch (addr) { 17917d87775fSJoe Komlodi case R_I3C1_REG1: 17927d87775fSJoe Komlodi case R_I3C2_REG1: 17937d87775fSJoe Komlodi case R_I3C3_REG1: 17947d87775fSJoe Komlodi case R_I3C4_REG1: 17957d87775fSJoe Komlodi case R_I3C5_REG1: 17967d87775fSJoe Komlodi case R_I3C6_REG1: 17977d87775fSJoe Komlodi if (data & R_I3C1_REG1_I2C_MODE_MASK) { 17987d87775fSJoe Komlodi qemu_log_mask(LOG_UNIMP, 17997d87775fSJoe Komlodi "%s: Unsupported I2C mode [0x%08" HWADDR_PRIx 18007d87775fSJoe Komlodi "]=%08" PRIx64 "\n", 18017d87775fSJoe Komlodi __func__, addr << 2, data); 18027d87775fSJoe Komlodi break; 18037d87775fSJoe Komlodi } 18047d87775fSJoe Komlodi if (data & R_I3C1_REG1_SA_EN_MASK) { 18057d87775fSJoe Komlodi qemu_log_mask(LOG_UNIMP, 18067d87775fSJoe Komlodi "%s: Unsupported slave mode [%08" HWADDR_PRIx 18077d87775fSJoe Komlodi "]=0x%08" PRIx64 "\n", 18087d87775fSJoe Komlodi __func__, addr << 2, data); 18097d87775fSJoe Komlodi break; 18107d87775fSJoe Komlodi } 18117d87775fSJoe Komlodi s->regs[addr] = data; 18127d87775fSJoe Komlodi break; 18137d87775fSJoe Komlodi default: 18147d87775fSJoe Komlodi s->regs[addr] = data; 18157d87775fSJoe Komlodi break; 18167d87775fSJoe Komlodi } 18177d87775fSJoe Komlodi } 18187d87775fSJoe Komlodi 18197d87775fSJoe Komlodi static const MemoryRegionOps aspeed_i3c_ops = { 18207d87775fSJoe Komlodi .read = aspeed_i3c_read, 18217d87775fSJoe Komlodi .write = aspeed_i3c_write, 18227d87775fSJoe Komlodi .endianness = DEVICE_LITTLE_ENDIAN, 18237d87775fSJoe Komlodi .valid = { 18247d87775fSJoe Komlodi .min_access_size = 1, 18257d87775fSJoe Komlodi .max_access_size = 4, 18267d87775fSJoe Komlodi } 18277d87775fSJoe Komlodi }; 18287d87775fSJoe Komlodi 18297d87775fSJoe Komlodi static void aspeed_i3c_reset(DeviceState *dev) 18307d87775fSJoe Komlodi { 18317d87775fSJoe Komlodi AspeedI3CState *s = ASPEED_I3C(dev); 18327d87775fSJoe Komlodi memset(s->regs, 0, sizeof(s->regs)); 18337d87775fSJoe Komlodi } 18347d87775fSJoe Komlodi 18357d87775fSJoe Komlodi static void aspeed_i3c_instance_init(Object *obj) 18367d87775fSJoe Komlodi { 18377d87775fSJoe Komlodi AspeedI3CState *s = ASPEED_I3C(obj); 18387d87775fSJoe Komlodi int i; 18397d87775fSJoe Komlodi 18407d87775fSJoe Komlodi for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) { 18417d87775fSJoe Komlodi object_initialize_child(obj, "device[*]", &s->devices[i], 18427d87775fSJoe Komlodi TYPE_ASPEED_I3C_DEVICE); 18437d87775fSJoe Komlodi } 18447d87775fSJoe Komlodi } 18457d87775fSJoe Komlodi 18467d87775fSJoe Komlodi static void aspeed_i3c_realize(DeviceState *dev, Error **errp) 18477d87775fSJoe Komlodi { 18487d87775fSJoe Komlodi int i; 18497d87775fSJoe Komlodi AspeedI3CState *s = ASPEED_I3C(dev); 18507d87775fSJoe Komlodi SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 18517d87775fSJoe Komlodi 18527d87775fSJoe Komlodi memory_region_init(&s->iomem_container, OBJECT(s), 18537d87775fSJoe Komlodi TYPE_ASPEED_I3C ".container", 0x8000); 18547d87775fSJoe Komlodi 18557d87775fSJoe Komlodi sysbus_init_mmio(sbd, &s->iomem_container); 18567d87775fSJoe Komlodi 18577d87775fSJoe Komlodi memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s, 18587d87775fSJoe Komlodi TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2); 18597d87775fSJoe Komlodi 18607d87775fSJoe Komlodi memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); 18617d87775fSJoe Komlodi 18627d87775fSJoe Komlodi for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) { 18637d87775fSJoe Komlodi Object *i3c_dev = OBJECT(&s->devices[i]); 18647d87775fSJoe Komlodi 18657d87775fSJoe Komlodi if (!object_property_set_uint(i3c_dev, "device-id", i, errp)) { 18667d87775fSJoe Komlodi return; 18677d87775fSJoe Komlodi } 18687d87775fSJoe Komlodi 18697d87775fSJoe Komlodi if (!sysbus_realize(SYS_BUS_DEVICE(i3c_dev), errp)) { 18707d87775fSJoe Komlodi return; 18717d87775fSJoe Komlodi } 18727d87775fSJoe Komlodi 18737d87775fSJoe Komlodi /* 18747d87775fSJoe Komlodi * Register Address of I3CX Device = 18757d87775fSJoe Komlodi * (Base Address of Global Register) + (Offset of I3CX) + Offset 18767d87775fSJoe Komlodi * X = 0, 1, 2, 3, 4, 5 18777d87775fSJoe Komlodi * Offset of I3C0 = 0x2000 18787d87775fSJoe Komlodi * Offset of I3C1 = 0x3000 18797d87775fSJoe Komlodi * Offset of I3C2 = 0x4000 18807d87775fSJoe Komlodi * Offset of I3C3 = 0x5000 18817d87775fSJoe Komlodi * Offset of I3C4 = 0x6000 18827d87775fSJoe Komlodi * Offset of I3C5 = 0x7000 18837d87775fSJoe Komlodi */ 18847d87775fSJoe Komlodi memory_region_add_subregion(&s->iomem_container, 18857d87775fSJoe Komlodi 0x2000 + i * 0x1000, &s->devices[i].mr); 18867d87775fSJoe Komlodi } 18877d87775fSJoe Komlodi 18887d87775fSJoe Komlodi } 18897d87775fSJoe Komlodi 18907d87775fSJoe Komlodi static Property aspeed_i3c_device_properties[] = { 18917d87775fSJoe Komlodi DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0), 18927d87775fSJoe Komlodi DEFINE_PROP_END_OF_LIST(), 18937d87775fSJoe Komlodi }; 18947d87775fSJoe Komlodi 18957d87775fSJoe Komlodi static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data) 18967d87775fSJoe Komlodi { 18977d87775fSJoe Komlodi DeviceClass *dc = DEVICE_CLASS(klass); 18987d87775fSJoe Komlodi 18997d87775fSJoe Komlodi dc->desc = "Aspeed I3C Device"; 19007d87775fSJoe Komlodi dc->realize = aspeed_i3c_device_realize; 19017d87775fSJoe Komlodi device_class_set_legacy_reset(dc, aspeed_i3c_device_reset); 19027d87775fSJoe Komlodi device_class_set_props(dc, aspeed_i3c_device_properties); 19037d87775fSJoe Komlodi } 19047d87775fSJoe Komlodi 19057d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_device_info = { 19067d87775fSJoe Komlodi .name = TYPE_ASPEED_I3C_DEVICE, 19077d87775fSJoe Komlodi .parent = TYPE_SYS_BUS_DEVICE, 19087d87775fSJoe Komlodi .instance_size = sizeof(AspeedI3CDevice), 19097d87775fSJoe Komlodi .class_init = aspeed_i3c_device_class_init, 19107d87775fSJoe Komlodi }; 19117d87775fSJoe Komlodi 19127d87775fSJoe Komlodi static const VMStateDescription vmstate_aspeed_i3c = { 19137d87775fSJoe Komlodi .name = TYPE_ASPEED_I3C, 19147d87775fSJoe Komlodi .version_id = 1, 19157d87775fSJoe Komlodi .minimum_version_id = 1, 19167d87775fSJoe Komlodi .fields = (const VMStateField[]) { 19177d87775fSJoe Komlodi VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS), 19187d87775fSJoe Komlodi VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1, 19197d87775fSJoe Komlodi aspeed_i3c_device_vmstate, AspeedI3CDevice), 19207d87775fSJoe Komlodi VMSTATE_END_OF_LIST(), 19217d87775fSJoe Komlodi } 19227d87775fSJoe Komlodi }; 19237d87775fSJoe Komlodi 19247d87775fSJoe Komlodi static void aspeed_i3c_class_init(ObjectClass *klass, void *data) 19257d87775fSJoe Komlodi { 19267d87775fSJoe Komlodi DeviceClass *dc = DEVICE_CLASS(klass); 19277d87775fSJoe Komlodi 19287d87775fSJoe Komlodi dc->realize = aspeed_i3c_realize; 19297d87775fSJoe Komlodi device_class_set_legacy_reset(dc, aspeed_i3c_reset); 19307d87775fSJoe Komlodi dc->desc = "Aspeed I3C Controller"; 19317d87775fSJoe Komlodi dc->vmsd = &vmstate_aspeed_i3c; 19327d87775fSJoe Komlodi } 19337d87775fSJoe Komlodi 19347d87775fSJoe Komlodi static const TypeInfo aspeed_i3c_info = { 19357d87775fSJoe Komlodi .name = TYPE_ASPEED_I3C, 19367d87775fSJoe Komlodi .parent = TYPE_SYS_BUS_DEVICE, 19377d87775fSJoe Komlodi .instance_init = aspeed_i3c_instance_init, 19387d87775fSJoe Komlodi .instance_size = sizeof(AspeedI3CState), 19397d87775fSJoe Komlodi .class_init = aspeed_i3c_class_init, 19407d87775fSJoe Komlodi }; 19417d87775fSJoe Komlodi 19427d87775fSJoe Komlodi static void aspeed_i3c_register_types(void) 19437d87775fSJoe Komlodi { 19447d87775fSJoe Komlodi type_register_static(&aspeed_i3c_device_info); 19457d87775fSJoe Komlodi type_register_static(&aspeed_i3c_info); 19467d87775fSJoe Komlodi } 19477d87775fSJoe Komlodi 19487d87775fSJoe Komlodi type_init(aspeed_i3c_register_types); 1949