>From: Chris Rauer <[email protected]> > >Reviewed-by: Hao Wu <[email protected]> >Signed-off-by: Chris Rauer <[email protected]> >Link: >https://lore.kernel.org/qemu-devel/[email protected] >[jms: rebase and minor build fixes for class_init and reset callback] >Signed-off-by: Joel Stanley <[email protected]> >--- >This is a re-submission of the model with Chris' permission, with a >light touch of updates to make it build with qemu master. > >It will be used by the Tenstorrent Atlantis machine, which will include >a functional test for i2c devices attached to the controller. > >Daniel suggested that Alistair would take it through the riscv tree as >a (soft) pre-req for the Atlantis machine. > >--- > MAINTAINERS | 6 + > include/hw/i2c/designware_i2c.h | 101 ++++ > hw/i2c/designware_i2c.c | 813 ++++++++++++++++++++++++++++++++ > hw/i2c/Kconfig | 4 + > hw/i2c/meson.build | 1 + > 5 files changed, 925 insertions(+) > create mode 100644 include/hw/i2c/designware_i2c.h > create mode 100644 hw/i2c/designware_i2c.c > >diff --git a/MAINTAINERS b/MAINTAINERS >index 9a55b649e8b4..b5a918426473 100644 >--- a/MAINTAINERS >+++ b/MAINTAINERS >@@ -2668,6 +2668,12 @@ S: Orphaned > F: hw/gpio/pcf8574.c > F: include/gpio/pcf8574.h > >+DesignWare I2C >+M: Chris Rauer <[email protected]> >+S: Maintained >+F: hw/i2c/designware_i2c.c >+F: include/hw/i2c/designware_i2c.h >+
Maybe here can also add Joel and me as reviewer, cause we are all familiar with this model. It's just a suggestion. > Generic Loader > M: Alistair Francis <[email protected]> > S: Maintained >diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h >new file mode 100644 >index 000000000000..0d8f904f51b7 >--- /dev/null >+++ b/include/hw/i2c/designware_i2c.h >@@ -0,0 +1,101 @@ >+/* >+ * DesignWare I2C Module. >+ * >+ * Copyright 2021 Google LLC >+ * >+ * SPDX-License-Identifier: GPL-2.0-or-later >+ */ >+#ifndef DESIGNWARE_I2C_H >+#define DESIGNWARE_I2C_H >+ >+#include "hw/i2c/i2c.h" >+#include "hw/core/irq.h" >+#include "hw/core/sysbus.h" >+ >+/* Size of the FIFO buffers. */ >+#define DESIGNWARE_I2C_RX_FIFO_SIZE 16 >+#define DESIGNWARE_I2C_TX_FIFO_SIZE 16 >+ >+typedef enum DesignWareI2CStatus { >+ DW_I2C_STATUS_IDLE, >+ DW_I2C_STATUS_SENDING_ADDRESS, >+ DW_I2C_STATUS_SENDING, >+ DW_I2C_STATUS_RECEIVING, >+} DesignWareI2CStatus; >+ >+/* >+ * struct DesignWareI2CState - DesignWare I2C device state. >+ * @bus: The underlying I2C Bus >+ * @irq: GIC interrupt line to fire on events >+ * @ic_con: : I2C control register >+ * @ic_tar: I2C target address register >+ * @ic_sar: I2C slave address register >+ * @ic_ss_scl_hcnt: Standard speed i2c clock scl high count register >+ * @ic_ss_scl_lcnt: Standard speed i2c clock scl low count register >+ * @ic_fs_scl_hcnt: Fast mode or fast mode plus i2c clock scl high count >+ * register >+ * @ic_fs_scl_lcnt:Fast mode or fast mode plus i2c clock scl low count >+ * register >+ * @ic_intr_mask: I2C Interrupt Mask Register >+ * @ic_raw_intr_stat: I2C raw interrupt status register >+ * @ic_rx_tl: I2C receive FIFO threshold register >+ * @ic_tx_tl: I2C transmit FIFO threshold register >+ * @ic_enable: I2C enable register >+ * @ic_status: I2C status register >+ * @ic_txflr: I2C transmit fifo level register >+ * @ic_rxflr: I2C receive fifo level register >+ * @ic_sda_hold: I2C SDA hold time length register >+ * @ic_tx_abrt_source: The I2C transmit abort source register >+ * @ic_sda_setup: I2C SDA setup register >+ * @ic_enable_status: I2C enable status register >+ * @ic_fs_spklen: I2C SS, FS or FM+ spike suppression limit >+ * @ic_comp_param_1: Component parameter register >+ * @ic_comp_version: I2C component version register >+ * @ic_comp_type: I2C component type register >+ * @rx_fifo: The FIFO buffer for receiving in FIFO mode. >+ * @rx_cur: The current position of rx_fifo. >+ * @status: The current status of the SMBus. >+ */ >+typedef struct DesignWareI2CState { >+ SysBusDevice parent; >+ >+ MemoryRegion iomem; >+ >+ I2CBus *bus; >+ qemu_irq irq; >+ >+ uint32_t ic_con; >+ uint32_t ic_tar; >+ uint32_t ic_sar; >+ uint32_t ic_ss_scl_hcnt; >+ uint32_t ic_ss_scl_lcnt; >+ uint32_t ic_fs_scl_hcnt; >+ uint32_t ic_fs_scl_lcnt; >+ uint32_t ic_intr_mask; >+ uint32_t ic_raw_intr_stat; >+ uint32_t ic_rx_tl; >+ uint32_t ic_tx_tl; >+ uint32_t ic_enable; >+ uint32_t ic_status; >+ uint32_t ic_txflr; >+ uint32_t ic_rxflr; >+ uint32_t ic_sda_hold; >+ uint32_t ic_tx_abrt_source; >+ uint32_t ic_sda_setup; >+ uint32_t ic_enable_status; >+ uint32_t ic_fs_spklen; >+ uint32_t ic_comp_param_1; >+ uint32_t ic_comp_version; >+ uint32_t ic_comp_type; >+ >+ uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE]; >+ uint8_t rx_cur; >+ >+ DesignWareI2CStatus status; >+} DesignWareI2CState; >+ >+#define TYPE_DESIGNWARE_I2C "designware-i2c" >+#define DESIGNWARE_I2C(obj) OBJECT_CHECK(DesignWareI2CState, (obj), \ >+ TYPE_DESIGNWARE_I2C) >+ >+#endif /* DESIGNWARE_I2C_H */ >diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c >new file mode 100644 >index 000000000000..2e808f61f050 >--- /dev/null >+++ b/hw/i2c/designware_i2c.c >@@ -0,0 +1,813 @@ >+/* >+ * DesignWare I2C Module. >+ * >+ * Copyright 2021 Google LLC >+ * >+ * SPDX-License-Identifier: GPL-2.0-or-later >+ */ >+ >+#include "qemu/osdep.h" >+ >+#include "hw/i2c/designware_i2c.h" >+#include "migration/vmstate.h" >+#include "qemu/bitops.h" >+#include "qemu/guest-random.h" >+#include "qemu/log.h" >+#include "qemu/module.h" >+#include "qemu/units.h" >+ >+enum DesignWareI2CRegister { >+ DW_IC_CON = 0x00, Perhaps this writing style is a bit more uniform in qemu: REG32(DW_IC_CON, 0x00) FIELD(DW_IC_CON, MASTER_MODE, 0, 1) ...... >+ DW_IC_TAR = 0x04, >+ DW_IC_SAR = 0x08, >+ DW_IC_DATA_CMD = 0x10, >+ DW_IC_SS_SCL_HCNT = 0x14, >+ DW_IC_SS_SCL_LCNT = 0x18, >+ DW_IC_FS_SCL_HCNT = 0x1c, >+ DW_IC_FS_SCL_LCNT = 0x20, >+ DW_IC_INTR_STAT = 0x2c, >+ DW_IC_INTR_MASK = 0x30, >+ DW_IC_RAW_INTR_STAT = 0x34, >+ DW_IC_RX_TL = 0x38, >+ DW_IC_TX_TL = 0x3c, >+ DW_IC_CLR_INTR = 0x40, >+ DW_IC_CLR_RX_UNDER = 0x44, >+ DW_IC_CLR_RX_OVER = 0x48, >+ DW_IC_CLR_TX_OVER = 0x4c, >+ DW_IC_CLR_RD_REQ = 0x50, >+ DW_IC_CLR_TX_ABRT = 0x54, >+ DW_IC_CLR_RX_DONE = 0x58, >+ DW_IC_CLR_ACTIVITY = 0x5c, >+ DW_IC_CLR_STOP_DET = 0x60, >+ DW_IC_CLR_START_DET = 0x64, >+ DW_IC_CLR_GEN_CALL = 0x68, >+ DW_IC_ENABLE = 0x6c, >+ DW_IC_STATUS = 0x70, >+ DW_IC_TXFLR = 0x74, >+ DW_IC_RXFLR = 0x78, >+ DW_IC_SDA_HOLD = 0x7c, >+ DW_IC_TX_ABRT_SOURCE = 0x80, >+ DW_IC_SLV_DATA_NACK_ONLY = 0x84, >+ DW_IC_DMA_CR = 0x88, >+ DW_IC_DMA_TDLR = 0x8c, >+ DW_IC_DMA_RDLR = 0x90, >+ DW_IC_SDA_SETUP = 0x94, >+ DW_IC_ACK_GENERAL_CALL = 0x98, >+ DW_IC_ENABLE_STATUS = 0x9c, >+ DW_IC_FS_SPKLEN = 0xa0, >+ DW_IC_CLR_RESTART_DET = 0xa8, >+ DW_IC_COMP_PARAM_1 = 0xf4, >+ DW_IC_COMP_VERSION = 0xf8, >+ DW_IC_COMP_TYPE = 0xfc, >+}; >+ >+/* DW_IC_CON fields */ >+#define DW_IC_CON_STOP_DET_IF_MASTER_ACTIV BIT(10) >+#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) >+#define DW_IC_CON_TX_EMPTY_CTRL BIT(8) >+#define DW_IC_CON_STOP_IF_ADDRESSED BIT(7) >+#define DW_IC_CON_SLAVE_DISABLE BIT(6) >+#define DW_IC_CON_IC_RESTART_EN BIT(5) >+#define DW_IC_CON_10BITADDR_MASTER BIT(4) >+#define DW_IC_CON_10BITADDR_SLAVE BIT(3) >+#define DW_IC_CON_SPEED(rv) extract32((rv), 1, 2) >+#define DW_IC_CON_MASTER_MODE BIT(0) >+ >+/* DW_IC_TAR fields */ >+#define DW_IC_TAR_IC_10BITADDR_MASTER BIT(12) >+#define DW_IC_TAR_SPECIAL BIT(11) >+#define DW_IC_TAR_GC_OR_START BIT(10) >+#define DW_IC_TAR_ADDRESS(rv) extract32((rv), 0, 10) >+ >+/* DW_IC_DATA_CMD fields */ >+#define DW_IC_DATA_CMD_RESTART BIT(10) >+#define DW_IC_DATA_CMD_STOP BIT(9) >+#define DW_IC_DATA_CMD_CMD BIT(8) >+#define DW_IC_DATA_CMD_DAT(rv) extract32((rv), 0, 8) >+ >+/* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */ >+#define DW_IC_INTR_RESTART_DET BIT(12) >+#define DW_IC_INTR_GEN_CALL BIT(11) >+#define DW_IC_INTR_START_DET BIT(10) >+#define DW_IC_INTR_STOP_DET BIT(9) >+#define DW_IC_INTR_ACTIVITY BIT(8) >+#define DW_IC_INTR_RX_DONE BIT(7) >+#define DW_IC_INTR_TX_ABRT BIT(6) >+#define DW_IC_INTR_RD_REQ BIT(5) >+#define DW_IC_INTR_TX_EMPTY BIT(4) /* Hardware clear only. */ >+#define DW_IC_INTR_TX_OVER BIT(3) >+#define DW_IC_INTR_RX_FULL BIT(2) /* Hardware clear only. */ >+#define DW_IC_INTR_RX_OVER BIT(1) >+#define DW_IC_INTR_RX_UNDER BIT(0) >+ >+/* DW_IC_ENABLE fields */ >+#define DW_IC_ENABLE_TX_CMD_BLOCK BIT(2) >+#define DW_IC_ENABLE_ABORT BIT(1) >+#define DW_IC_ENABLE_ENABLE BIT(0) >+ >+/* DW_IC_STATUS fields */ >+#define DW_IC_STATUS_SLV_ACTIVITY BIT(6) >+#define DW_IC_STATUS_MST_ACTIVITY BIT(5) >+#define DW_IC_STATUS_RFF BIT(4) >+#define DW_IC_STATUS_RFNE BIT(3) >+#define DW_IC_STATUS_TFE BIT(2) >+#define DW_IC_STATUS_TFNF BIT(1) >+#define DW_IC_STATUS_ACTIVITY BIT(0) >+ >+/* DW_IC_TX_ABRT_SOURCE fields */ >+#define DW_IC_TX_TX_FLUSH_CNT extract32((rv), 23, 9) >+#define DW_IC_TX_ABRT_USER_ABRT BIT(16) >+#define DW_IC_TX_ABRT_SLVRD_INTX BIT(15) >+#define DW_IC_TX_ABRT_SLV_ARBLOST BIT(14) >+#define DW_IC_TX_ABRT_SLVFLUSH_TXFIFO BIT(13) >+#define DW_IC_TX_ARB_LOST BIT(12) >+#define DW_IC_TX_ABRT_MASTER_DIS BIT(11) >+#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(10) >+#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(9) >+#define DW_IC_TX_ABRT_HS_NORSTRT BIT(8) >+#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(7) >+#define DW_IC_TX_ABRT_HS_ACKDET BIT(6) >+#define DW_IC_TX_ABRT_GCALL_READ BIT(5) >+#define DW_IC_TX_ABRT_GCALL_NOACK BIT(4) >+#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(3) >+#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(2) >+#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(1) >+#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(0) >+ >+ >+/* IC_ENABLE_STATUS fields */ >+#define DW_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2) >+#define DW_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1) >+#define DW_IC_ENABLE_STATUS_IC_EN BIT(0) >+ >+/* Masks for writable registers. */ >+#define DW_IC_CON_MASK 0x000003ff >+#define DW_IC_TAR_MASK 0x00000fff >+#define DW_IC_SAR_MASK 0x000003ff >+#define DW_IC_SS_SCL_HCNT_MASK 0x0000ffff >+#define DW_IC_SS_SCL_LCNT_MASK 0x0000ffff >+#define DW_IC_FS_SCL_HCNT_MASK 0x0000ffff >+#define DW_IC_FS_SCL_LCNT_MASK 0x0000ffff >+#define DW_IC_INTR_MASK_MASK 0x00001fff >+#define DW_IC_ENABLE_MASK 0x00000007 >+#define DW_IC_SDA_HOLD_MASK 0x00ffffff >+#define DW_IC_SDA_SETUP_MASK 0x000000ff >+#define DW_IC_FS_SPKLEN_MASK 0x000000ff >+ >+/* Reset values */ >+#define DW_IC_CON_INIT_VAL 0x7d >+#define DW_IC_TAR_INIT_VAL 0x1055 >+#define DW_IC_SAR_INIT_VAL 0x55 >+#define DW_IC_SS_SCL_HCNT_INIT_VAL 0x190 >+#define DW_IC_SS_SCL_LCNT_INIT_VAL 0x1d6 >+#define DW_IC_FS_SCL_HCNT_INIT_VAL 0x3c >+#define DW_IC_FS_SCL_LCNT_INIT_VAL 0x82 >+#define DW_IC_INTR_MASK_INIT_VAL 0x8ff >+#define DW_IC_STATUS_INIT_VAL 0x6 >+#define DW_IC_SDA_HOLD_INIT_VAL 0x1 >+#define DW_IC_SDA_SETUP_INIT_VAL 0x64 >+#define DW_IC_FS_SPKLEN_INIT_VAL 0x2 >+ >+#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7) >+#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */ >+#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5) >+#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */ >+#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3)) >+#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */ >+#define DW_IC_COMP_PARAM_1_INIT_VAL \ >+ (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \ >+ DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \ >+ DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \ >+ DW_IC_COMP_PARAM_1_INTR_IO | \ >+ DW_IC_COMP_PARAM_1_HAS_DMA | \ >+ DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \ >+ ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \ >+ ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16)) >+#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a >+#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140 >+ >+static void dw_i2c_update_irq(DesignWareI2CState *s) >+{ >+ int level; >+ uint32_t intr = s->ic_raw_intr_stat & s->ic_intr_mask; >+ >+ level = !!((intr & DW_IC_INTR_RX_UNDER) | >+ (intr & DW_IC_INTR_RX_OVER) | >+ (intr & DW_IC_INTR_RX_FULL) | >+ (intr & DW_IC_INTR_TX_OVER) | >+ (intr & DW_IC_INTR_TX_EMPTY) | >+ (intr & DW_IC_INTR_RD_REQ) | >+ (intr & DW_IC_INTR_TX_ABRT) | >+ (intr & DW_IC_INTR_RX_DONE) | >+ (intr & DW_IC_INTR_ACTIVITY) | >+ (intr & DW_IC_INTR_STOP_DET) | >+ (intr & DW_IC_INTR_START_DET) | >+ (intr & DW_IC_INTR_GEN_CALL) | >+ (intr & DW_IC_INTR_RESTART_DET) >+ ); >+ qemu_set_irq(s->irq, level); >+} >+ >+static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s) >+{ >+ uint32_t value = s->rx_fifo[s->rx_cur]; >+ >+ if (s->status != DW_I2C_STATUS_RECEIVING) { >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: Attempted to read from RX fifo when not in receive >" >+ "state.\n", DEVICE(s)->canonical_path); >+ if (s->status != DW_I2C_STATUS_IDLE) { >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; >+ dw_i2c_update_irq(s); >+ } >+ return 0; >+ } >+ >+ s->rx_cur = (s->rx_cur + 1) % DESIGNWARE_I2C_RX_FIFO_SIZE; >+ >+ if (s->ic_rxflr > 0) { >+ s->ic_rxflr--; >+ } else { >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; >+ dw_i2c_update_irq(s); >+ return 0; >+ } >+ >+ if (s->ic_rxflr <= s->ic_rx_tl) { >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; >+ dw_i2c_update_irq(s); >+ } >+ >+ return value; >+} >+ >+static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size) >+{ >+ uint64_t value = 0; >+ >+ DesignWareI2CState *s = opaque; >+ >+ switch (offset) { >+ case DW_IC_CON: >+ value = s->ic_con; >+ break; >+ case DW_IC_TAR: >+ value = s->ic_tar; >+ break; >+ case DW_IC_SAR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_sar\n", >+ DEVICE(s)->canonical_path); >+ value = s->ic_sar; >+ break; >+ case DW_IC_DATA_CMD: >+ value = dw_i2c_read_ic_data_cmd(s); >+ break; >+ case DW_IC_SS_SCL_HCNT: >+ value = s->ic_ss_scl_hcnt; >+ break; >+ case DW_IC_SS_SCL_LCNT: >+ value = s->ic_ss_scl_lcnt; >+ break; >+ case DW_IC_FS_SCL_HCNT: >+ value = s->ic_fs_scl_hcnt; >+ break; >+ case DW_IC_FS_SCL_LCNT: >+ value = s->ic_fs_scl_lcnt; >+ break; >+ case DW_IC_INTR_STAT: >+ value = s->ic_raw_intr_stat & s->ic_intr_mask; >+ break; >+ case DW_IC_INTR_MASK: >+ value = s->ic_intr_mask; >+ break; >+ case DW_IC_RAW_INTR_STAT: >+ value = s->ic_raw_intr_stat; >+ break; >+ case DW_IC_RX_TL: >+ value = s->ic_rx_tl; >+ break; >+ case DW_IC_TX_TL: >+ value = s->ic_tx_tl; >+ break; >+ case DW_IC_CLR_INTR: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL | >+ DW_IC_INTR_RESTART_DET | >+ DW_IC_INTR_START_DET | >+ DW_IC_INTR_STOP_DET | >+ DW_IC_INTR_ACTIVITY | >+ DW_IC_INTR_RX_DONE | >+ DW_IC_INTR_TX_ABRT | >+ DW_IC_INTR_RD_REQ | >+ DW_IC_INTR_TX_OVER | >+ DW_IC_INTR_RX_OVER | >+ DW_IC_INTR_RX_UNDER); >+ s->ic_tx_abrt_source = 0; >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_RX_UNDER: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_UNDER); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_RX_OVER: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_OVER); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_TX_OVER: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_OVER); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_RD_REQ: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RD_REQ); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_TX_ABRT: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_ABRT); >+ s->ic_tx_abrt_source = 0; >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_RX_DONE: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_DONE); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_ACTIVITY: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_ACTIVITY); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_STOP_DET: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_STOP_DET); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_START_DET: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_START_DET); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_CLR_GEN_CALL: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL); >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_ENABLE: >+ value = s->ic_enable; >+ break; >+ case DW_IC_STATUS: >+ value = s->ic_status; >+ break; >+ case DW_IC_TXFLR: >+ value = s->ic_txflr; >+ break; >+ case DW_IC_RXFLR: >+ value = s->ic_rxflr; >+ break; >+ case DW_IC_SDA_HOLD: >+ value = s->ic_sda_hold; >+ break; >+ case DW_IC_TX_ABRT_SOURCE: >+ value = s->ic_tx_abrt_source; >+ break; >+ case DW_IC_SLV_DATA_NACK_ONLY: >+ qemu_log_mask(LOG_UNIMP, >+ "%s: unsupported read - ic_slv_data_nack_only\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_CR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_cr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_TDLR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_tdlr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_RDLR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_rdlr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_SDA_SETUP: >+ value = s->ic_sda_setup; >+ break; >+ case DW_IC_ACK_GENERAL_CALL: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - >ic_ack_general_call\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_ENABLE_STATUS: >+ value = s->ic_enable_status; >+ break; >+ case DW_IC_FS_SPKLEN: >+ value = s->ic_fs_spklen; >+ break; >+ case DW_IC_CLR_RESTART_DET: >+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET); >+ break; >+ case DW_IC_COMP_PARAM_1: >+ value = s->ic_comp_param_1; >+ break; >+ case DW_IC_COMP_VERSION: >+ value = s->ic_comp_version; >+ break; >+ case DW_IC_COMP_TYPE: >+ value = s->ic_comp_type; >+ break; >+ >+ /* This register is invalid at this point. */ >+ default: >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", >+ DEVICE(s)->canonical_path, offset); >+ break; >+ } >+ Here may add a trace function? Just like other i2c model do in trace-events. This is convenient for debugging. >+ return value; >+} >+ >+static void dw_i2c_write_ic_con(DesignWareI2CState *s, uint32_t value) >+{ >+ if (value & DW_IC_CON_RX_FIFO_FULL_HLD_CTRL) { >+ qemu_log_mask(LOG_UNIMP, >+ "%s: unsupported ic_con flag - RX_FIFO_FULL_HLD_CTRL\n", >+ DEVICE(s)->canonical_path); >+ } >+ >+ if (!(s->ic_enable & DW_IC_ENABLE_ENABLE)) { >+ s->ic_con = value & DW_IC_CON_MASK; >+ } else { >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: invalid setting to ic_con %d when >ic_enable[0]==1\n", >+ DEVICE(s)->canonical_path, value); >+ } >+} >+ >+static void dw_i2c_reset_to_idle(DesignWareI2CState *s) >+{ >+ s->ic_enable_status &= ~DW_IC_ENABLE_STATUS_IC_EN; >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY; >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_OVER; >+ s->ic_rxflr = 0; >+ s->ic_status &= ~DW_IC_STATUS_ACTIVITY; >+ s->status = DW_I2C_STATUS_IDLE; >+ dw_i2c_update_irq(s); >+} >+ >+static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src) >+{ >+ s->ic_tx_abrt_source |= src; >+ s->ic_raw_intr_stat |= DW_IC_INTR_TX_ABRT; >+ dw_i2c_reset_to_idle(s); >+ dw_i2c_update_irq(s); >+} >+ >+static void dw_i2c_write_ic_data_cmd(DesignWareI2CState *s, uint32_t value) >+{ >+ int recv = !!(value & DW_IC_DATA_CMD_CMD); >+ >+ if (s->status == DW_I2C_STATUS_IDLE || >+ s->ic_raw_intr_stat & DW_IC_INTR_TX_ABRT) { >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: Attempted to write to TX fifo when it is held in " >+ "reset.\n", DEVICE(s)->canonical_path); >+ return; >+ } >+ >+ /* Send the address if it hasn't been sent yet. */ >+ if (s->status == DW_I2C_STATUS_SENDING_ADDRESS) { >+ int rv = i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), >recv); >+ if (rv) { >+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK); >+ return; >+ } >+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; >+ } >+ >+ /* Send data */ >+ if (!recv) { >+ int rv = i2c_send(s->bus, DW_IC_DATA_CMD_DAT(value)); >+ if (rv) { >+ i2c_end_transfer(s->bus); >+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_TXDATA_NOACK); >+ return; >+ } >+ dw_i2c_update_irq(s); >+ } >+ >+ /* Restart command */ >+ if (value & DW_IC_DATA_CMD_RESTART && s->ic_con & >DW_IC_CON_IC_RESTART_EN) { >+ s->ic_raw_intr_stat |= DW_IC_INTR_RESTART_DET | >+ DW_IC_INTR_START_DET | >+ DW_IC_INTR_ACTIVITY; >+ s->ic_status |= DW_IC_STATUS_ACTIVITY; >+ dw_i2c_update_irq(s); >+ >+ if (i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv)) { >+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK); >+ return; >+ } >+ >+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; >+ } >+ >+ /* Receive data */ >+ if (recv) { >+ uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE; >+ >+ if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) { >+ s->rx_fifo[pos] = i2c_recv(s->bus); >+ s->ic_rxflr++; >+ } else { >+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_OVER; >+ dw_i2c_update_irq(s); >+ } >+ >+ if (s->ic_rxflr > s->ic_rx_tl) { >+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL; >+ dw_i2c_update_irq(s); >+ } >+ if (value & DW_IC_DATA_CMD_STOP) { >+ i2c_nack(s->bus); >+ } >+ } >+ >+ /* Stop command */ >+ if (value & DW_IC_DATA_CMD_STOP) { >+ s->ic_raw_intr_stat |= DW_IC_INTR_STOP_DET; >+ s->ic_status &= ~DW_IC_STATUS_ACTIVITY; >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY; >+ i2c_end_transfer(s->bus); >+ dw_i2c_update_irq(s); >+ } >+} >+ >+static void dw_i2c_write_ic_enable(DesignWareI2CState *s, uint32_t value) >+{ >+ if (value & DW_IC_ENABLE_ENABLE && !(s->ic_con & >DW_IC_CON_SLAVE_DISABLE)) { >+ qemu_log_mask(LOG_UNIMP, >+ "%s: Designware I2C slave mode is not supported.\n", >+ DEVICE(s)->canonical_path); >+ return; >+ } >+ >+ s->ic_enable = value & DW_IC_ENABLE_MASK; >+ >+ if (value & DW_IC_ENABLE_ABORT || value & DW_IC_ENABLE_TX_CMD_BLOCK) { >+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_USER_ABRT); >+ return; >+ } >+ >+ if (value & DW_IC_ENABLE_ENABLE) { >+ s->ic_enable_status |= DW_IC_ENABLE_STATUS_IC_EN; >+ s->ic_status |= DW_IC_STATUS_ACTIVITY; >+ s->ic_raw_intr_stat |= DW_IC_INTR_ACTIVITY | >+ DW_IC_INTR_START_DET | >+ DW_IC_INTR_TX_EMPTY; >+ s->status = DW_I2C_STATUS_SENDING_ADDRESS; >+ dw_i2c_update_irq(s); >+ } else if ((value & DW_IC_ENABLE_ENABLE) == 0) { >+ dw_i2c_reset_to_idle(s); >+ } >+ >+} >+ >+static void dw_i2c_write_ic_rx_tl(DesignWareI2CState *s, uint32_t value) >+{ >+ /* Note that a value of 0 for ic_rx_tl indicates a threashold of 1. */ >+ if (value > DESIGNWARE_I2C_RX_FIFO_SIZE - 1) { >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: invalid setting to ic_rx_tl %d\n", >+ DEVICE(s)->canonical_path, value); >+ s->ic_rx_tl = DESIGNWARE_I2C_RX_FIFO_SIZE - 1; >+ } else { >+ s->ic_rx_tl = value; >+ } >+ >+ if (s->ic_rxflr > s->ic_rx_tl && s->ic_enable & DW_IC_ENABLE_ENABLE) { >+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL; >+ } else { >+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; >+ } >+ dw_i2c_update_irq(s); >+} >+ >+static void dw_i2c_write_ic_tx_tl(DesignWareI2CState *s, uint32_t value) >+{ >+ /* >+ * Note that a value of 0 for ic_tx_tl indicates a threashold of 1. >+ * However, the tx threshold is not used in the model because commands are >+ * always sent out as soon as they are written. >+ */ >+ if (value > DESIGNWARE_I2C_TX_FIFO_SIZE - 1) { >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: invalid setting to ic_tx_tl %d\n", >+ DEVICE(s)->canonical_path, value); >+ s->ic_tx_tl = DESIGNWARE_I2C_TX_FIFO_SIZE - 1; >+ } else { >+ s->ic_tx_tl = value; >+ } >+} >+ >+static void dw_i2c_write(void *opaque, hwaddr offset, uint64_t value, >+ unsigned size) >+{ >+ DesignWareI2CState *s = opaque; >+ Trace function like above. >+ /* The order of the registers are their order in memory. */ >+ switch (offset) { >+ case DW_IC_CON: >+ dw_i2c_write_ic_con(s, value); >+ break; >+ case DW_IC_TAR: >+ s->ic_tar = value & DW_IC_TAR_MASK; >+ break; >+ case DW_IC_SAR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n", >+ DEVICE(s)->canonical_path); >+ s->ic_sar = value & DW_IC_SAR_MASK; >+ break; >+ case DW_IC_DATA_CMD: >+ dw_i2c_write_ic_data_cmd(s, value); >+ break; >+ case DW_IC_SS_SCL_HCNT: >+ s->ic_ss_scl_hcnt = value & DW_IC_SS_SCL_HCNT_MASK; >+ break; >+ case DW_IC_SS_SCL_LCNT: >+ s->ic_ss_scl_lcnt = value & DW_IC_SS_SCL_LCNT_MASK; >+ break; >+ case DW_IC_FS_SCL_HCNT: >+ s->ic_fs_scl_hcnt = value & DW_IC_FS_SCL_HCNT_MASK; >+ break; >+ case DW_IC_FS_SCL_LCNT: >+ s->ic_fs_scl_lcnt = value & DW_IC_FS_SCL_LCNT_MASK; >+ break; >+ case DW_IC_INTR_MASK: >+ s->ic_intr_mask = value & DW_IC_INTR_MASK_MASK; >+ dw_i2c_update_irq(s); >+ break; >+ case DW_IC_RX_TL: >+ dw_i2c_write_ic_rx_tl(s, value); >+ break; >+ case DW_IC_TX_TL: >+ dw_i2c_write_ic_tx_tl(s, value); >+ break; >+ case DW_IC_ENABLE: >+ dw_i2c_write_ic_enable(s, value); >+ break; >+ case DW_IC_SDA_HOLD: >+ s->ic_sda_hold = value & DW_IC_SDA_HOLD_MASK; >+ break; >+ case DW_IC_SLV_DATA_NACK_ONLY: >+ qemu_log_mask(LOG_UNIMP, >+ "%s: unsupported write - ic_slv_data_nack_only\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_CR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_cr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_TDLR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_tdlr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_DMA_RDLR: >+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_rdlr\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_SDA_SETUP: >+ s->ic_sda_setup = value & DW_IC_SDA_SETUP_MASK; >+ break; >+ case DW_IC_ACK_GENERAL_CALL: >+ qemu_log_mask(LOG_UNIMP, >+ "%s: unsupported write - ic_ack_general_call\n", >+ DEVICE(s)->canonical_path); >+ break; >+ case DW_IC_FS_SPKLEN: >+ s->ic_fs_spklen = value & DW_IC_FS_SPKLEN_MASK; >+ break; >+ >+ /* This register is invalid at this point. */ >+ default: >+ qemu_log_mask(LOG_GUEST_ERROR, >+ "%s: write to invalid offset or readonly register 0x%" >+ HWADDR_PRIx "\n", >+ DEVICE(s)->canonical_path, offset); >+ break; >+ } >+} >+ >+static const MemoryRegionOps designware_i2c_ops = { >+ .read = dw_i2c_read, >+ .write = dw_i2c_write, >+ .endianness = DEVICE_LITTLE_ENDIAN, >+ .valid = { >+ .min_access_size = 4, >+ .max_access_size = 4, >+ .unaligned = false, >+ }, >+}; >+ >+static void designware_i2c_enter_reset(Object *obj, ResetType type) >+{ >+ DesignWareI2CState *s = DESIGNWARE_I2C(obj); >+ >+ s->ic_con = DW_IC_CON_INIT_VAL; >+ s->ic_tar = DW_IC_TAR_INIT_VAL; >+ s->ic_sar = DW_IC_SAR_INIT_VAL; >+ s->ic_ss_scl_hcnt = DW_IC_SS_SCL_HCNT_INIT_VAL; >+ s->ic_ss_scl_lcnt = DW_IC_SS_SCL_LCNT_INIT_VAL; >+ s->ic_fs_scl_hcnt = DW_IC_FS_SCL_HCNT_INIT_VAL; >+ s->ic_fs_scl_lcnt = DW_IC_FS_SCL_LCNT_INIT_VAL; >+ s->ic_intr_mask = DW_IC_INTR_MASK_INIT_VAL; >+ s->ic_raw_intr_stat = 0; >+ s->ic_rx_tl = 0; >+ s->ic_tx_tl = 0; >+ s->ic_enable = 0; >+ s->ic_status = DW_IC_STATUS_INIT_VAL; >+ s->ic_txflr = 0; >+ s->ic_rxflr = 0; >+ s->ic_sda_hold = DW_IC_SDA_HOLD_INIT_VAL; >+ s->ic_tx_abrt_source = 0; >+ s->ic_sda_setup = DW_IC_SDA_SETUP_INIT_VAL; >+ s->ic_enable_status = 0; >+ s->ic_fs_spklen = DW_IC_FS_SPKLEN_INIT_VAL; >+ s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL; >+ s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL; >+ s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL; >+ >+ s->rx_cur = 0; >+ s->status = DW_I2C_STATUS_IDLE; >+} >+ >+static void designware_i2c_hold_reset(Object *obj, ResetType type) >+{ >+ DesignWareI2CState *s = DESIGNWARE_I2C(obj); >+ >+ qemu_irq_lower(s->irq); >+} >+ >+static const VMStateDescription vmstate_designware_i2c = { >+ .name = TYPE_DESIGNWARE_I2C, >+ .version_id = 0, >+ .minimum_version_id = 0, >+ .fields = (VMStateField[]) { >+ VMSTATE_UINT32(ic_con, DesignWareI2CState), >+ VMSTATE_UINT32(ic_tar, DesignWareI2CState), >+ VMSTATE_UINT32(ic_sar, DesignWareI2CState), >+ VMSTATE_UINT32(ic_ss_scl_hcnt, DesignWareI2CState), >+ VMSTATE_UINT32(ic_ss_scl_lcnt, DesignWareI2CState), >+ VMSTATE_UINT32(ic_fs_scl_hcnt, DesignWareI2CState), >+ VMSTATE_UINT32(ic_fs_scl_lcnt, DesignWareI2CState), >+ VMSTATE_UINT32(ic_intr_mask, DesignWareI2CState), >+ VMSTATE_UINT32(ic_raw_intr_stat, DesignWareI2CState), >+ VMSTATE_UINT32(ic_rx_tl, DesignWareI2CState), >+ VMSTATE_UINT32(ic_tx_tl, DesignWareI2CState), >+ VMSTATE_UINT32(ic_enable, DesignWareI2CState), >+ VMSTATE_UINT32(ic_status, DesignWareI2CState), >+ VMSTATE_UINT32(ic_txflr, DesignWareI2CState), >+ VMSTATE_UINT32(ic_rxflr, DesignWareI2CState), >+ VMSTATE_UINT32(ic_sda_hold, DesignWareI2CState), >+ VMSTATE_UINT32(ic_tx_abrt_source, DesignWareI2CState), >+ VMSTATE_UINT32(ic_sda_setup, DesignWareI2CState), >+ VMSTATE_UINT32(ic_enable_status, DesignWareI2CState), >+ VMSTATE_UINT32(ic_fs_spklen, DesignWareI2CState), >+ VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState), >+ VMSTATE_UINT32(ic_comp_version, DesignWareI2CState), >+ VMSTATE_UINT32(ic_comp_type, DesignWareI2CState), >+ VMSTATE_UINT32(status, DesignWareI2CState), >+ VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState, >+ DESIGNWARE_I2C_RX_FIFO_SIZE), >+ VMSTATE_UINT8(rx_cur, DesignWareI2CState), >+ VMSTATE_END_OF_LIST(), >+ }, >+}; >+ >+static void designware_i2c_smbus_init(Object *obj) >+{ >+ DesignWareI2CState *s = DESIGNWARE_I2C(obj); >+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >+ >+ sysbus_init_irq(sbd, &s->irq); >+ >+ memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s, >+ "regs", 4 * KiB); >+ sysbus_init_mmio(sbd, &s->iomem); >+ >+ s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); >+} >+ >+static void designware_i2c_class_init(ObjectClass *klass, const void *data) >+{ >+ ResettableClass *rc = RESETTABLE_CLASS(klass); >+ DeviceClass *dc = DEVICE_CLASS(klass); >+ >+ dc->desc = "Designware I2C"; >+ dc->vmsd = &vmstate_designware_i2c; >+ rc->phases.enter = designware_i2c_enter_reset; >+ rc->phases.hold = designware_i2c_hold_reset; >+} >+ >+static const TypeInfo designware_i2c_types[] = { >+ { >+ .name = TYPE_DESIGNWARE_I2C, >+ .parent = TYPE_SYS_BUS_DEVICE, >+ .instance_size = sizeof(DesignWareI2CState), >+ .class_init = designware_i2c_class_init, >+ .instance_init = designware_i2c_smbus_init, >+ }, >+}; >+DEFINE_TYPES(designware_i2c_types); >diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig >index 596a7a3165ad..d3f394edeb9c 100644 >--- a/hw/i2c/Kconfig >+++ b/hw/i2c/Kconfig >@@ -18,6 +18,10 @@ config ARM_SBCON_I2C > bool > select BITBANG_I2C > >+config DESIGNWARE_I2C >+ bool >+ select I2C >+ > config ACPI_SMBUS > bool > select SMBUS >diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build >index c459adcb596c..88aea35662dd 100644 >--- a/hw/i2c/meson.build >+++ b/hw/i2c/meson.build >@@ -11,6 +11,7 @@ i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: >files('mpc_i2c.c')) > i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c')) > i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c')) > i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c')) >+i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c')) > i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c')) > i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c')) > i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) >-- >2.47.3 >
