drivers: i2c: npcx: prevent unexpected target address match ISR

This commit prevents the hardware from generating an unexpected
target slave address match ISR by the following change:
1. Enable the New Match Interrupt Enable bit (NMINTEN) only when
   necessary.
2. Explicitly clear all SMBnADDRx registers because they are not
   cleared when the I2C hardware is disabled. It will cause the
   asynchrinization between SMBnADDRx and registered_target_mask if the
   system jumps from the RO image to the RW image.

Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
Signed-off-by: Alvis Sun <yfsun@nuvoton.com>
This commit is contained in:
Jun Lin 2025-06-12 17:31:54 +08:00 committed by Benjamin Cabé
commit 8ab712a235

View file

@ -403,6 +403,37 @@ static inline void i2c_ctrl_fifo_clear_status(const struct device *dev)
inst->SMBFIF_CTS |= BIT(NPCX_SMBFIF_CTS_CLR_FIFO);
}
/* I2C target reg access */
#ifdef CONFIG_I2C_TARGET
static volatile uint8_t *npcx_i2c_ctrl_target_get_reg_smbaddr(const struct device *i2c_dev,
int index)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(i2c_dev);
switch (index) {
case 0:
return &inst->SMBADDR1;
case 1:
return &inst->SMBADDR2;
case 2:
return &inst->SMBADDR3;
case 3:
return &inst->SMBADDR4;
case 4:
return &inst->SMBADDR5;
case 5:
return &inst->SMBADDR6;
case 6:
return &inst->SMBADDR7;
case 7:
return &inst->SMBADDR8;
default:
LOG_ERR("Invalid SMBADDR index: %d", index);
return NULL;
}
}
#endif /* CONFIG_I2C_TARGET */
/*
* I2C local functions which touch the registers in 'Normal' bank. These
* utilities will change bank back to FIFO mode when leaving themselves in case
@ -433,6 +464,16 @@ static void i2c_ctrl_init_module(const struct device *dev)
/* Enable module - before configuring CTL1 */
inst->SMBCTL2 |= BIT(NPCX_SMBCTL2_ENABLE);
#ifdef CONFIG_I2C_TARGET
volatile uint8_t *reg_smbaddr;
/* Clear all the SMBnADDR */
for (int i = 0; i < NPCX_I2C_FLAG_COUNT; i++) {
reg_smbaddr = npcx_i2c_ctrl_target_get_reg_smbaddr(dev, i);
*reg_smbaddr = 0;
}
#endif
/* Enable SMB interrupt and 'New Address Match' interrupt source */
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
@ -1194,33 +1235,6 @@ recover_exit:
}
#ifdef CONFIG_I2C_TARGET
static volatile uint8_t *npcx_i2c_ctrl_target_get_reg_smbaddr(const struct device *i2c_dev,
int index)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(i2c_dev);
switch (index) {
case 0:
return &inst->SMBADDR1;
case 1:
return &inst->SMBADDR2;
case 2:
return &inst->SMBADDR3;
case 3:
return &inst->SMBADDR4;
case 4:
return &inst->SMBADDR5;
case 5:
return &inst->SMBADDR6;
case 6:
return &inst->SMBADDR7;
case 7:
return &inst->SMBADDR8;
default:
LOG_ERR("Invalid SMBADDR index: %d", index);
return NULL;
}
}
int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
struct i2c_target_config *target_cfg, uint8_t port)
@ -1372,7 +1386,7 @@ int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
i2c_ctrl_bank_sel(i2c_dev, NPCX_I2C_BANK_FIFO);
/* Reconfigure SMBCTL1 */
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_INTEN);
/* Disable irq of smb wake-up event */
if (IS_ENABLED(CONFIG_PM)) {
@ -1392,14 +1406,9 @@ int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
static void i2c_target_wk_isr(const struct device *dev, struct npcx_wui *wui)
{
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
/* Clear wake up detection event status */
npcx_i2c_target_clear_detection_event();
/* Reconfigure SMBCTL1 */
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
/*
* Suspend-to-idle stops SMB module clocks (derived from APB2/APB3), which must remain
* active during a transaction.