drivers: i3c: npcx: add HDR-DDR mode for transfer
1. Support HDR-DDR DMA transfer.
2. Remove polling mode in transfer.
Signed-off-by: Alvis Sun <yfsun@nuvoton.com>
diff --git a/drivers/i3c/i3c_npcx.c b/drivers/i3c/i3c_npcx.c
index 9790e59..75b9c73 100644
--- a/drivers/i3c/i3c_npcx.c
+++ b/drivers/i3c/i3c_npcx.c
@@ -39,11 +39,14 @@
#define MCLKD_FREQ_45_MHZ MHZ(45)
-#define I3C_STATUS_CLR_MASK \
- (BIT(NPCX_I3C_MSTATUS_TGTSTART) | BIT(NPCX_I3C_MSTATUS_MCTRLDONE) | \
- BIT(NPCX_I3C_MSTATUS_COMPLETE) | BIT(NPCX_I3C_MSTATUS_IBIWON) | \
+#define I3C_STATUS_CLR_MASK \
+ (BIT(NPCX_I3C_MSTATUS_TGTSTART) | BIT(NPCX_I3C_MSTATUS_MCTRLDONE) | \
+ BIT(NPCX_I3C_MSTATUS_COMPLETE) | BIT(NPCX_I3C_MSTATUS_IBIWON) | \
BIT(NPCX_I3C_MSTATUS_NOWCNTLR))
+#define HDR_DDR_CMD_AND_CRC_SZ_WORD 0x2 /* 2 words = Command(1 word) + CRC(1 word) */
+#define HDR_RD_CMD 0x80
+
/* Supported I3C MCLKD frequency */
enum npcx_i3c_speed {
NPCX_I3C_BUS_SPEED_45MHZ,
@@ -217,7 +220,7 @@
static bool npcx_i3c_has_error(struct i3c_reg *inst)
{
if (IS_BIT_SET(inst->MSTATUS, NPCX_I3C_MSTATUS_ERRWARN)) {
- LOG_WRN("ERROR: MSTATUS 0x%08x MERRWARN 0x%08x", inst->MSTATUS, inst->MERRWARN);
+ LOG_ERR("ERROR: MSTATUS 0x%08x MERRWARN 0x%08x", inst->MSTATUS, inst->MERRWARN);
return true;
}
@@ -314,7 +317,8 @@
* param[in] addr Dyamic address for xfer or 0x7E for CCC command.
* param[in] op_type Request type.
* param[in] is_read Read(true) or write(false) operation.
- * param[in] read_sz Read size.
+ * param[in] read_sz Read size in bytes.
+ * If op_tye is HDR-DDR, the read_sz must be the number of words.
*
* return 0, success
* else, error
@@ -396,6 +400,56 @@
return 0;
}
+static inline int npcx_i3c_request_hdr_exit(struct i3c_reg *inst)
+{
+ uint32_t val = 0;
+ uint32_t state;
+ int ret;
+
+ /* Before sending the HDR exit command, check the HDR mode */
+ state = npcx_i3c_state_get(inst);
+ if (state != MSTATUS_STATE_MSGDDR) {
+ LOG_ERR("%s, state error: %#x", __func__, state);
+ return -EPERM;
+ }
+
+ SET_FIELD(val, NPCX_I3C_MCTRL_TYPE, MCTRL_TYPE_HDR_EXIT);
+ SET_FIELD(val, NPCX_I3C_MCTRL_REQUEST, MCTRL_REQUEST_FORCEEXIT);
+
+ ret = npcx_i3c_send_request(inst, val);
+ if (ret != 0) {
+ LOG_ERR("Request hdr exit error %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int npcx_i3c_xfer_stop(struct i3c_reg *inst)
+{
+ uint32_t state;
+ int ret;
+
+ state = npcx_i3c_state_get(inst);
+ LOG_DBG("%s, state=%d", __func__, state);
+
+ switch (state) {
+ case MSTATUS_STATE_NORMACT: /* SDR */
+ ret = npcx_i3c_request_emit_stop(inst);
+ break;
+ case MSTATUS_STATE_MSGDDR: /* HDR-DDR */
+ ret = npcx_i3c_request_hdr_exit(inst);
+ break;
+ default:
+ /* Not supported */
+ ret = -ENOTSUP;
+ LOG_WRN("xfer_stop state not supported, state:%d", state);
+ break;
+ }
+
+ return ret;
+}
+
static inline int npcx_i3c_ibi_respond_nack(struct i3c_reg *inst)
{
uint32_t val = 0;
@@ -668,6 +722,7 @@
/*
* brief: Perform DMA read transaction.
+ * (Data width used for DMA transfers is "byte")
*
* For read end, use the MDMA end-of-transfer interrupt(SIEN bit)
* instead of using the I3CI interrupt generated by COMPLETE bit in MSTATUS register.
@@ -717,6 +772,7 @@
/*
* brief: Perform one transfer transaction by DMA.
+ * (Support SDR and HDR-DDR)
*
* param[in] inst Pointer to controller registers.
* param[in] addr Target address.
@@ -731,18 +787,45 @@
*/
static int npcx_i3c_do_one_xfer_dma(const struct device *dev, uint8_t addr,
enum npcx_i3c_mctrl_type op_type, uint8_t *buf, size_t buf_sz,
- bool is_read, bool emit_start, bool emit_stop)
+ bool is_read, bool emit_start, bool emit_stop, uint8_t hdr_cmd)
{
const struct npcx_i3c_config *config = dev->config;
struct i3c_reg *inst = config->base;
int ret = 0;
+ bool is_hdr_ddr = (op_type == NPCX_I3C_MCTRL_TYPE_I3C_HDR_DDR) ? true : false;
+ size_t rd_len = buf_sz;
npcx_i3c_status_clear_all(inst);
npcx_i3c_errwarn_clear_all(inst);
+ /* Check HDR-DDR moves data by words */
+ if (is_hdr_ddr && (buf_sz % 2 != 0)) {
+ LOG_ERR("%s, HDR-DDR data length should be even, len=%#x", __func__, buf_sz);
+ return -EINVAL;
+ }
+
/* Emit START if needed */
if (emit_start) {
- ret = npcx_i3c_request_emit_start(inst, addr, op_type, is_read, buf_sz);
+ /*
+ * For HDR-DDR mode read, RDTERM also includes one word (16 bits) for CRC.
+ * For example, to read 8 bytes, set RDTERM to 6.
+ * (1 word HDR-DDR command + 4 words data + 1 word for CRC)
+ */
+ if (is_hdr_ddr) {
+ if (is_read) {
+ /* The unit of rd_len is "word" in DDR mode */
+ rd_len /= sizeof(uint16_t); /* byte to word */
+ rd_len += HDR_DDR_CMD_AND_CRC_SZ_WORD;
+ hdr_cmd |= HDR_RD_CMD;
+ } else {
+ hdr_cmd &= ~HDR_RD_CMD;
+ }
+
+ /* Write the command code for the HDR-DDR message */
+ inst->MWDATAB = hdr_cmd;
+ }
+
+ ret = npcx_i3c_request_emit_start(inst, addr, op_type, is_read, rd_len);
if (ret != 0) {
LOG_ERR("%s: emit start fail", __func__);
goto out_do_one_xfer_dma;
@@ -773,9 +856,9 @@
}
out_do_one_xfer_dma:
- /* Emit STOP if needed */
+ /* Emit STOP or exit DDR if needed */
if (emit_stop) {
- npcx_i3c_request_emit_stop(inst);
+ npcx_i3c_xfer_stop(inst);
}
return ret;
@@ -784,6 +867,7 @@
/*
* brief: Perform one transfer transaction.
+ * (Support SDR only)
*
* param[in] inst Pointer to controller registers.
* param[in] addr Target address.
@@ -882,10 +966,12 @@
{
const struct npcx_i3c_config *config = dev->config;
struct i3c_reg *inst = config->base;
+ struct npcx_i3c_data *data = dev->data;
uint32_t intmask;
int xfered_len, ret = 0;
bool send_broadcast = true;
bool is_xfer_done = true;
+ enum npcx_i3c_mctrl_type op_type;
if (msgs == NULL) {
return -EINVAL;
@@ -913,7 +999,6 @@
/* Iterate over all the messages */
for (int i = 0; i < num_msgs; i++) {
-
/*
* Check message is read or write operaion.
* For write operation, check the last data byte of a transmit message.
@@ -958,35 +1043,63 @@
}
#endif
- /*
- * Two ways to do read/write transfer .
- * 1. [S] + [0x7E] + [address] + [data] + [Sr or P]
- * 2. [S] + [address] + [data] + [Sr or P]
- *
- * Send broadcast header(0x7E) on first transfer or after a STOP,
- * unless flag is set not to.
- */
- if (!(msgs[i].flags & I3C_MSG_NBCH) && (send_broadcast)) {
- ret = npcx_i3c_request_emit_start(inst, I3C_BROADCAST_ADDR,
- NPCX_I3C_MCTRL_TYPE_I3C, false, 0);
- if (ret < 0) {
- LOG_ERR("%s: emit start of broadcast addr failed, error (%d)",
- __func__, ret);
+ /* Check message SDR or HDR mode */
+ bool is_msg_hdr = (msgs[i].flags & I3C_MSG_HDR) == I3C_MSG_HDR;
+
+ /* Set emit start type SDR or HDR-DDR mode */
+ if (!is_msg_hdr || msgs[i].hdr_mode == 0) {
+ op_type = NPCX_I3C_MCTRL_TYPE_I3C; /* Set operation type SDR */
+
+ /*
+ * SDR, send boradcast header(0x7E)
+ *
+ * Two ways to do read/write transfer (SDR mode).
+ * 1. [S] + [0x7E] + [address] + [data] + [Sr or P]
+ * 2. [S] + [address] + [data] + [Sr or P]
+ *
+ * Send broadcast header(0x7E) on first transfer or after a STOP,
+ * unless flag is set not to.
+ */
+ if (!(msgs[i].flags & I3C_MSG_NBCH) && send_broadcast) {
+ ret = npcx_i3c_request_emit_start(inst, I3C_BROADCAST_ADDR,
+ NPCX_I3C_MCTRL_TYPE_I3C, false,
+ 0);
+ if (ret < 0) {
+ LOG_ERR("%s: emit start of broadcast addr failed, error "
+ "(%d)",
+ __func__, ret);
+ break;
+ }
+ send_broadcast = false;
+ }
+ } else if ((data->common.ctrl_config.supported_hdr & I3C_MSG_HDR_DDR) &&
+ (msgs[i].hdr_mode == I3C_MSG_HDR_DDR) && is_msg_hdr) {
+
+ op_type = NPCX_I3C_MCTRL_TYPE_I3C_HDR_DDR; /* Set operation type DDR */
+
+ /* Check HDR-DDR moves data by words */
+ if ((msgs[i].len % 2) != 0x0) {
+ LOG_ERR("HDR-DDR data length should be number of words , xfer "
+ "len=%d", msgs[i].num_xfer);
+ ret = -EINVAL;
break;
}
- send_broadcast = false;
+ } else {
+ LOG_ERR("%s: %s controller HDR Mode %#x\r\n"
+ "msg HDR mode %#x, msg flag %#x",
+ __func__, dev->name, data->common.ctrl_config.supported_hdr,
+ msgs[i].hdr_mode, msgs[i].flags);
+ ret = -ENOTSUP;
+ break;
}
#ifdef CONFIG_I3C_NPCX_DMA
/* Do transfer with target device */
- xfered_len = npcx_i3c_do_one_xfer_dma(dev, target->dynamic_addr,
- NPCX_I3C_MCTRL_TYPE_I3C, msgs[i].buf,
- msgs[i].len, is_read, emit_start, emit_stop);
-#else
- xfered_len = npcx_i3c_do_one_xfer(inst, target->dynamic_addr,
- NPCX_I3C_MCTRL_TYPE_I3C, msgs[i].buf, msgs[i].len,
- is_read, emit_start, emit_stop, no_ending);
+ xfered_len = npcx_i3c_do_one_xfer_dma(dev, target->dynamic_addr, op_type,
+ msgs[i].buf, msgs[i].len, is_read, emit_start,
+ emit_stop, msgs[i].hdr_cmd_code);
#endif
+
if (xfered_len < 0) {
LOG_ERR("%s: do xfer fail", __func__);
ret = xfered_len; /* Set error code to ret */
@@ -997,7 +1110,7 @@
msgs[i].num_xfer = xfered_len;
if (emit_stop) {
- /* After a STOP, send broadcast header before next msg */
+ /* SDR. After a STOP, send broadcast header before next msg */
send_broadcast = true;
}
@@ -1009,7 +1122,7 @@
/* Emit stop if error occurs or stop flag not in the msg */
if ((ret != 0) || (is_xfer_done == false)) {
- npcx_i3c_request_emit_stop(inst);
+ npcx_i3c_xfer_stop(inst);
}
npcx_i3c_errwarn_clear_all(inst);
@@ -1681,6 +1794,8 @@
#ifdef CONFIG_I3C_USE_IBI
/* Target start detected */
if (IS_BIT_SET(inst->MSTATUS, NPCX_I3C_MSTATUS_TGTSTART)) {
+ LOG_DBG("ISR TGTSTART !");
+
/* Disable further target initiated IBI interrupt */
inst->MINTCLR = BIT(NPCX_I3C_MINTCLR_TGTSTART);
@@ -1958,7 +2073,7 @@
}
ctrl_config->is_secondary = false; /* Currently can only act as primary controller. */
- ctrl_config->supported_hdr = 0U; /* HDR mode not supported at the moment. */
+ ctrl_config->supported_hdr = I3C_MSG_HDR_DDR; /* HDR-DDR mode is supported. */
ctrl_config->scl.i3c = config->clocks.i3c_pp_scl_hz; /* Set I3C frequency */
ret = npcx_i3c_configure(dev, I3C_CONFIG_CONTROLLER, ctrl_config);
diff --git a/soc/nuvoton/npcx/common/reg/reg_def.h b/soc/nuvoton/npcx/common/reg/reg_def.h
index ae02a6e..b43c3d5 100644
--- a/soc/nuvoton/npcx/common/reg/reg_def.h
+++ b/soc/nuvoton/npcx/common/reg/reg_def.h
@@ -1913,12 +1913,17 @@
#define MCTRL_IBIRESP_ACK_MANDATORY 2 /* ACK with mandatory byte */
#define MCTRL_IBIRESP_MANUAL 3
+/* For REQUEST = EmitStartAddr */
enum npcx_i3c_mctrl_type {
NPCX_I3C_MCTRL_TYPE_I3C,
NPCX_I3C_MCTRL_TYPE_I2C,
NPCX_I3C_MCTRL_TYPE_I3C_HDR_DDR,
};
+/* For REQUEST = ForceExit/Target Reset */
+#define MCTRL_TYPE_HDR_EXIT 0
+#define MCTRL_TYPE_TGT_RESTART 2
+
/* MSTATUS options */
#define MSTATUS_STATE_IDLE 0x0
#define MSTATUS_STATE_TGTREQ 0x1