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>
This commit is contained in:
parent
4cec19952f
commit
03580e4a1b
2 changed files with 155 additions and 35 deletions
|
@ -39,11 +39,14 @@ LOG_MODULE_REGISTER(npcx_i3c, CONFIG_I3C_LOG_LEVEL);
|
|||
|
||||
#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 inline void npcx_i3c_interrupt_enable(struct i3c_reg *inst, uint32_t mask
|
|||
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 @@ static inline int npcx_i3c_request_auto_ibi(struct i3c_reg *inst)
|
|||
* 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 @@ static inline int npcx_i3c_request_emit_stop(struct i3c_reg *inst)
|
|||
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 @@ out_wr_fifo_dma:
|
|||
|
||||
/*
|
||||
* 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 @@ static int npcx_i3c_xfer_read_fifo_dma(const struct device *dev, uint8_t *buf, u
|
|||
|
||||
/*
|
||||
* 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_xfer_read_fifo_dma(const struct device *dev, uint8_t *buf, u
|
|||
*/
|
||||
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 @@ static int npcx_i3c_do_one_xfer_dma(const struct device *dev, uint8_t addr,
|
|||
}
|
||||
|
||||
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 @@ out_do_one_xfer_dma:
|
|||
|
||||
/*
|
||||
* brief: Perform one transfer transaction.
|
||||
* (Support SDR only)
|
||||
*
|
||||
* param[in] inst Pointer to controller registers.
|
||||
* param[in] addr Target address.
|
||||
|
@ -882,10 +966,12 @@ static int npcx_i3c_transfer(const struct device *dev, struct i3c_device_desc *t
|
|||
{
|
||||
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 @@ static int npcx_i3c_transfer(const struct device *dev, struct i3c_device_desc *t
|
|||
|
||||
/* 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 @@ static int npcx_i3c_transfer(const struct device *dev, struct i3c_device_desc *t
|
|||
}
|
||||
#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 @@ static int npcx_i3c_transfer(const struct device *dev, struct i3c_device_desc *t
|
|||
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 @@ static int npcx_i3c_transfer(const struct device *dev, struct i3c_device_desc *t
|
|||
|
||||
/* 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 @@ static void npcx_i3c_isr(const struct device *dev)
|
|||
#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 @@ static int npcx_i3c_init(const struct device *dev)
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -1913,12 +1913,17 @@ struct i3c_reg {
|
|||
#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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue