Skip to content

Commit 430d644

Browse files
committed
drivers: i2c: npcx: add support for multi-address in target mode
This commit introduces support for the NPCX I2C driver to handle up to 8 addresses in target mode Signed-off-by: Alvis Sun <yfsun@nuvoton.com>
1 parent a60d88e commit 430d644

File tree

2 files changed

+188
-50
lines changed

2 files changed

+188
-50
lines changed

drivers/i2c/i2c_npcx_controller.c

+168-48
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ LOG_MODULE_REGISTER(i2c_npcx, CONFIG_I2C_LOG_LEVEL);
108108
#define I2C_RECOVER_SCL_RETRY 10
109109
#define I2C_RECOVER_SDA_RETRY 3
110110

111+
#define NPCX_SMBADDR_SAEN NPCX_SMBADDR1_SAEN /* All the SAEN in SMBADDR is bit_7 */
112+
111113
/* Supported I2C bus frequency */
112114
enum npcx_i2c_freq {
113115
NPCX_I2C_BUS_SPEED_100KHZ,
@@ -116,7 +118,14 @@ enum npcx_i2c_freq {
116118
};
117119

118120
enum npcx_i2c_flag {
119-
NPCX_I2C_FLAG_TARGET,
121+
NPCX_I2C_FLAG_TARGET1,
122+
NPCX_I2C_FLAG_TARGET2,
123+
NPCX_I2C_FLAG_TARGET3,
124+
NPCX_I2C_FLAG_TARGET4,
125+
NPCX_I2C_FLAG_TARGET5,
126+
NPCX_I2C_FLAG_TARGET6,
127+
NPCX_I2C_FLAG_TARGET7,
128+
NPCX_I2C_FLAG_TARGET8,
120129
NPCX_I2C_FLAG_COUNT,
121130
};
122131

@@ -178,8 +187,9 @@ struct i2c_ctrl_data {
178187
bool is_configured; /* is port configured? */
179188
const struct npcx_i2c_timing_cfg *ptr_speed_confs;
180189
#ifdef CONFIG_I2C_TARGET
181-
struct i2c_target_config *target_cfg;
182-
atomic_t flags;
190+
struct i2c_target_config *target_cfg[NPCX_I2C_FLAG_COUNT];
191+
uint8_t target_idx; /* current target_cfg index */
192+
atomic_t registered_target_mask;
183193
/* i2c wake-up callback configuration */
184194
struct miwu_callback smb_wk_cb;
185195
#endif /* CONFIG_I2C_TARGET */
@@ -847,17 +857,19 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
847857
{
848858
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
849859
struct i2c_ctrl_data *const data = dev->data;
850-
const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks;
860+
const struct i2c_target_callbacks *target_cb = NULL;
851861
uint8_t val = 0;
852862

853863
/* A 'Bus Error' has been identified */
854864
if (IS_BIT_SET(status, NPCX_SMBST_BER)) {
855865
/* Clear BER Bit */
856866
inst->SMBST = BIT(NPCX_SMBST_BER);
857867

868+
target_cb = data->target_cfg[data->target_idx]->callbacks;
869+
858870
/* Notify upper layer the end of transaction */
859-
if (target_cb->stop) {
860-
target_cb->stop(data->target_cfg);
871+
if ((target_cb != NULL) && target_cb->stop) {
872+
target_cb->stop(data->target_cfg[data->target_idx]);
861873
}
862874

863875
/* Reset i2c module in target mode */
@@ -887,8 +899,9 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
887899
/* End of transaction */
888900
data->oper_state = NPCX_I2C_IDLE;
889901
/* Notify upper layer a STOP condition received */
890-
if (target_cb->stop) {
891-
target_cb->stop(data->target_cfg);
902+
target_cb = data->target_cfg[data->target_idx]->callbacks;
903+
if ((target_cb != NULL) && target_cb->stop) {
904+
target_cb->stop(data->target_cfg[data->target_idx]);
892905
}
893906

894907
#ifdef CONFIG_PM
@@ -910,39 +923,56 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
910923
/* Clear NMATCH Bit */
911924
inst->SMBST = BIT(NPCX_SMBST_NMATCH);
912925

926+
/* Check MATCH1F ~ MATCH7F */
927+
if (inst->SMBCST2 & ~BIT(NPCX_SMBCST2_INTSTS)) {
928+
for (uint8_t addr_idx = NPCX_I2C_FLAG_TARGET1;
929+
addr_idx <= NPCX_I2C_FLAG_TARGET7; addr_idx++) {
930+
if (inst->SMBCST2 & BIT(addr_idx)) {
931+
data->target_idx = addr_idx;
932+
break;
933+
}
934+
}
935+
} else if (inst->SMBCST3 & BIT(NPCX_SMBCST3_MATCHA8F)) {
936+
data->target_idx = NPCX_I2C_FLAG_TARGET8;
937+
}
938+
939+
target_cb = data->target_cfg[data->target_idx]->callbacks;
940+
913941
/* Distinguish the direction of i2c target mode by reading XMIT bit */
914942
if (IS_BIT_SET(inst->SMBST, NPCX_SMBST_XMIT)) {
915943
/* Start transmitting data in i2c target mode */
916944
data->oper_state = NPCX_I2C_WRITE_FIFO;
917945
/* Write first requested byte after repeated start */
918-
if (target_cb->read_requested) {
919-
target_cb->read_requested(data->target_cfg, &val);
946+
if ((target_cb != NULL) && target_cb->read_requested) {
947+
target_cb->read_requested(data->target_cfg[data->target_idx], &val);
920948
}
921949
inst->SMBSDA = val;
922950
} else {
923951
/* Start receiving data in i2c target mode */
924952
data->oper_state = NPCX_I2C_READ_FIFO;
925953

926-
if (target_cb->write_requested) {
927-
target_cb->write_requested(data->target_cfg);
954+
if ((target_cb != NULL) && target_cb->write_requested) {
955+
target_cb->write_requested(data->target_cfg[data->target_idx]);
928956
}
929957
}
930958
return;
931959
}
932960

933961
/* Tx byte empty or Rx byte full has occurred */
934962
if (IS_BIT_SET(status, NPCX_SMBST_SDAST)) {
963+
target_cb = data->target_cfg[data->target_idx]->callbacks;
964+
935965
if (data->oper_state == NPCX_I2C_WRITE_FIFO) {
936966
/* Notify upper layer one byte will be transmitted */
937-
if (target_cb->read_processed) {
938-
target_cb->read_processed(data->target_cfg, &val);
967+
if ((target_cb != NULL) && target_cb->read_processed) {
968+
target_cb->read_processed(data->target_cfg[data->target_idx], &val);
939969
}
940970
inst->SMBSDA = val;
941971
} else if (data->oper_state == NPCX_I2C_READ_FIFO) {
942-
if (target_cb->write_received) {
972+
if ((target_cb != NULL) && target_cb->write_received) {
943973
val = inst->SMBSDA;
944974
/* Notify upper layer one byte received */
945-
target_cb->write_received(data->target_cfg, val);
975+
target_cb->write_received(data->target_cfg[data->target_idx], val);
946976
}
947977
} else {
948978
LOG_ERR("Unexpected oper state %d on i2c target port%02x!",
@@ -971,7 +1001,7 @@ static void i2c_ctrl_isr(const struct device *dev)
9711001
LOG_DBG("status: %02x, %d", status, data->oper_state);
9721002

9731003
#ifdef CONFIG_I2C_TARGET
974-
if (atomic_test_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
1004+
if (atomic_get(&data->registered_target_mask) != (atomic_val_t) 0) {
9751005
i2c_ctrl_target_isr(dev, status);
9761006
return;
9771007
}
@@ -1170,6 +1200,34 @@ int npcx_i2c_ctrl_recover_bus(const struct device *dev)
11701200
}
11711201

11721202
#ifdef CONFIG_I2C_TARGET
1203+
static volatile uint8_t *npcx_i2c_ctrl_target_get_reg_smbaddr(const struct device *i2c_dev,
1204+
int index)
1205+
{
1206+
struct smb_reg *const inst = HAL_I2C_INSTANCE(i2c_dev);
1207+
1208+
switch (index) {
1209+
case 0:
1210+
return &inst->SMBADDR1;
1211+
case 1:
1212+
return &inst->SMBADDR2;
1213+
case 2:
1214+
return &inst->SMBADDR3;
1215+
case 3:
1216+
return &inst->SMBADDR4;
1217+
case 4:
1218+
return &inst->SMBADDR5;
1219+
case 5:
1220+
return &inst->SMBADDR6;
1221+
case 6:
1222+
return &inst->SMBADDR7;
1223+
case 7:
1224+
return &inst->SMBADDR8;
1225+
default:
1226+
LOG_ERR("Invalid SMBADDR index: %d", index);
1227+
return NULL;
1228+
}
1229+
}
1230+
11731231
int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
11741232
struct i2c_target_config *target_cfg, uint8_t port)
11751233
{
@@ -1178,22 +1236,50 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
11781236
struct i2c_ctrl_data *const data = i2c_dev->data;
11791237
int idx_ctrl = (port & 0xF0) >> 4;
11801238
int idx_port = (port & 0x0F);
1181-
uint8_t addr = BIT(NPCX_SMBADDR1_SAEN) | target_cfg->address;
1182-
1183-
/* I2c module has been configured to target mode */
1184-
if (atomic_test_and_set_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
1185-
return -EBUSY;
1186-
}
1239+
int avail_addr_slot;
1240+
volatile uint8_t *reg_smbaddr;
1241+
uint8_t smbaddr_val = BIT(NPCX_SMBADDR_SAEN) | target_cfg->address;
1242+
uint32_t i2c_tgt_mask = (uint32_t)atomic_get(&data->registered_target_mask);
1243+
int addr_idx;
11871244

11881245
/* A transiaction is ongoing */
11891246
if (data->oper_state != NPCX_I2C_IDLE) {
1190-
atomic_clear_bit(&data->flags, NPCX_I2C_FLAG_TARGET);
11911247
return -EBUSY;
11921248
}
11931249

1194-
data->target_cfg = target_cfg;
1250+
/* Find valid smbaddr location */
1251+
avail_addr_slot = find_lsb_set(~i2c_tgt_mask) - 1;
1252+
if (avail_addr_slot == -1 || avail_addr_slot >= NPCX_I2C_FLAG_COUNT) {
1253+
LOG_ERR("No available smbaddr register, smbaddr_idx: %d", avail_addr_slot);
1254+
return -ENOSPC;
1255+
}
1256+
1257+
/* Check if the address is duplicated */
1258+
while (i2c_tgt_mask) {
1259+
addr_idx = find_lsb_set(i2c_tgt_mask) - 1;
1260+
reg_smbaddr = npcx_i2c_ctrl_target_get_reg_smbaddr(i2c_dev, addr_idx);
1261+
1262+
/* Check if the address is duplicated */
1263+
if (*reg_smbaddr == smbaddr_val) {
1264+
LOG_ERR("Address %#x is already set", target_cfg->address);
1265+
return -EINVAL;
1266+
}
1267+
1268+
i2c_tgt_mask &= ~BIT(addr_idx);
1269+
}
1270+
1271+
/* Mark the selected address slot */
1272+
atomic_set_bit(&data->registered_target_mask, avail_addr_slot);
11951273

11961274
i2c_ctrl_irq_enable(i2c_dev, 0);
1275+
1276+
data->port = port; /* Update the I2C port index */
1277+
1278+
/* Config new address */
1279+
reg_smbaddr = npcx_i2c_ctrl_target_get_reg_smbaddr(i2c_dev, avail_addr_slot);
1280+
*reg_smbaddr = smbaddr_val; /* Set address register */
1281+
data->target_cfg[avail_addr_slot] = target_cfg; /* Set target config */
1282+
11971283
/* Switch correct port for i2c controller first */
11981284
npcx_pinctrl_i2c_port_sel(idx_ctrl, idx_port);
11991285
/* Reset I2C module */
@@ -1203,7 +1289,6 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
12031289
/* Select normal bank and single byte mode for i2c target mode */
12041290
i2c_ctrl_bank_sel(i2c_dev, NPCX_I2C_BANK_NORMAL);
12051291
inst->SMBFIF_CTL &= ~BIT(NPCX_SMBFIF_CTL_FIFO_EN);
1206-
inst->SMBADDR1 = addr; /* Enable target mode and configure its address */
12071292

12081293
/* Reconfigure SMBCTL1 */
12091294
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
@@ -1218,6 +1303,7 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
12181303
/* Enable SMB's MIWU interrupts */
12191304
npcx_miwu_irq_enable(&config->smb_wui);
12201305
}
1306+
12211307
i2c_ctrl_irq_enable(i2c_dev, 1);
12221308

12231309
return 0;
@@ -1230,44 +1316,78 @@ int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
12301316
const struct i2c_ctrl_config *const config = i2c_dev->config;
12311317
struct i2c_ctrl_data *const data = i2c_dev->data;
12321318
int idx_ctrl = (port & 0xF0) >> 4;
1319+
int cur_addr_slot;
1320+
volatile uint8_t *reg_smbaddr;
1321+
uint8_t smbaddr_val = BIT(NPCX_SMBADDR_SAEN) | target_cfg->address;
1322+
uint32_t i2c_tgt_mask = (uint32_t)atomic_get(&data->registered_target_mask);
12331323

12341324
/* No I2c module has been configured to target mode */
1235-
if (!atomic_test_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
1325+
if (atomic_get(&data->registered_target_mask) == (atomic_val_t) 0) {
1326+
LOG_ERR("No available target to ungister");
12361327
return -EINVAL;
12371328
}
12381329

12391330
/* A transiaction is ongoing */
12401331
if (data->oper_state != NPCX_I2C_IDLE) {
12411332
return -EBUSY;
12421333
}
1243-
data->target_cfg = NULL;
1334+
1335+
/* Find target address in smbaddr */
1336+
while (i2c_tgt_mask) {
1337+
cur_addr_slot = find_lsb_set(i2c_tgt_mask) - 1;
1338+
reg_smbaddr = npcx_i2c_ctrl_target_get_reg_smbaddr(i2c_dev, cur_addr_slot);
1339+
if (reg_smbaddr == NULL) {
1340+
LOG_ERR("Invalid smbaddr register");
1341+
return -EINVAL;
1342+
}
1343+
1344+
/* Target address found */
1345+
if (*reg_smbaddr == smbaddr_val) {
1346+
break;
1347+
}
1348+
1349+
i2c_tgt_mask &= ~BIT(cur_addr_slot);
1350+
}
1351+
1352+
/* Input addrss is not in the smbaddr */
1353+
if (i2c_tgt_mask == 0 || reg_smbaddr == NULL) {
1354+
LOG_ERR("Address %#x is not found", target_cfg->address);
1355+
return -EINVAL;
1356+
}
12441357

12451358
i2c_ctrl_irq_enable(i2c_dev, 0);
1246-
/* Reset I2C module */
1247-
inst->SMBCTL2 &= ~BIT(NPCX_SMBCTL2_ENABLE);
1248-
inst->SMBCTL2 |= BIT(NPCX_SMBCTL2_ENABLE);
12491359

1250-
inst->SMBADDR1 = 0; /* Disable target mode and clear address setting */
1251-
/* Enable FIFO mode and select to FIFO bank for i2c controller mode */
1252-
inst->SMBFIF_CTL |= BIT(NPCX_SMBFIF_CTL_FIFO_EN);
1253-
i2c_ctrl_bank_sel(i2c_dev, NPCX_I2C_BANK_FIFO);
1360+
*reg_smbaddr = 0; /* Disable target mode and clear address setting */
1361+
data->target_cfg[cur_addr_slot] = NULL; /* Clear target config */
12541362

1255-
/* Reconfigure SMBCTL1 */
1256-
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
1363+
/* Mark it as controller mode */
1364+
atomic_clear_bit(&data->registered_target_mask, cur_addr_slot);
12571365

1258-
/* Disable irq of smb wake-up event */
1259-
if (IS_ENABLED(CONFIG_PM)) {
1260-
/* Disable SMB wake up detection */
1261-
npcx_i2c_target_start_wk_enable(idx_ctrl, false);
1262-
/* Disable start detect in IDLE */
1263-
inst->SMBCTL3 &= ~BIT(NPCX_SMBCTL3_IDL_START);
1264-
/* Disable SMB's MIWU interrupts */
1265-
npcx_miwu_irq_disable(&config->smb_wui);
1366+
/* Switch I2C to controller mode if no any other valid address in smbaddr */
1367+
if (atomic_get(&data->registered_target_mask) == (atomic_val_t) 0) {
1368+
/* Reset I2C module */
1369+
inst->SMBCTL2 &= ~BIT(NPCX_SMBCTL2_ENABLE);
1370+
inst->SMBCTL2 |= BIT(NPCX_SMBCTL2_ENABLE);
12661371

1372+
/* Enable FIFO mode and select to FIFO bank for i2c controller mode */
1373+
inst->SMBFIF_CTL |= BIT(NPCX_SMBFIF_CTL_FIFO_EN);
1374+
i2c_ctrl_bank_sel(i2c_dev, NPCX_I2C_BANK_FIFO);
1375+
1376+
/* Reconfigure SMBCTL1 */
1377+
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
1378+
1379+
/* Disable irq of smb wake-up event */
1380+
if (IS_ENABLED(CONFIG_PM)) {
1381+
/* Disable SMB wake up detection */
1382+
npcx_i2c_target_start_wk_enable(idx_ctrl, false);
1383+
/* Disable start detect in IDLE */
1384+
inst->SMBCTL3 &= ~BIT(NPCX_SMBCTL3_IDL_START);
1385+
/* Disable SMB's MIWU interrupts */
1386+
npcx_miwu_irq_disable(&config->smb_wui);
1387+
}
12671388
}
1389+
12681390
i2c_ctrl_irq_enable(i2c_dev, 1);
1269-
/* Mark it as controller mode */
1270-
atomic_clear_bit(&data->flags, NPCX_I2C_FLAG_TARGET);
12711391

12721392
return 0;
12731393
}
@@ -1304,7 +1424,7 @@ int npcx_i2c_ctrl_transfer(const struct device *i2c_dev, struct i2c_msg *msgs,
13041424

13051425
#ifdef CONFIG_I2C_TARGET
13061426
/* I2c module has been configured to target mode */
1307-
if (atomic_test_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
1427+
if (atomic_get(&data->registered_target_mask) != (atomic_val_t) 0) {
13081428
return -EBUSY;
13091429
}
13101430
#endif /* CONFIG_I2C_TARGET */

0 commit comments

Comments
 (0)