/* * Copyright (c) 2023 Nuvoton Technology Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_numaker_i2c #include #include #include #include #include #include LOG_MODULE_REGISTER(i2c_numaker, CONFIG_I2C_LOG_LEVEL); #include "i2c-priv.h" #include #include /* i2c Master Mode Status */ #define M_START 0x08 /* Start */ #define M_REPEAT_START 0x10 /* Master Repeat Start */ #define M_TRAN_ADDR_ACK 0x18 /* Master Transmit Address ACK */ #define M_TRAN_ADDR_NACK 0x20 /* Master Transmit Address NACK */ #define M_TRAN_DATA_ACK 0x28 /* Master Transmit Data ACK */ #define M_TRAN_DATA_NACK 0x30 /* Master Transmit Data NACK */ #define M_ARB_LOST 0x38 /* Master Arbitration Los */ #define M_RECE_ADDR_ACK 0x40 /* Master Receive Address ACK */ #define M_RECE_ADDR_NACK 0x48 /* Master Receive Address NACK */ #define M_RECE_DATA_ACK 0x50 /* Master Receive Data ACK */ #define M_RECE_DATA_NACK 0x58 /* Master Receive Data NACK */ #define BUS_ERROR 0x00 /* Bus error */ /* i2c Slave Mode Status */ #define S_REPEAT_START_STOP 0xA0 /* Slave Transmit Repeat Start or Stop */ #define S_TRAN_ADDR_ACK 0xA8 /* Slave Transmit Address ACK */ #define S_TRAN_DATA_ACK 0xB8 /* Slave Transmit Data ACK */ #define S_TRAN_DATA_NACK 0xC0 /* Slave Transmit Data NACK */ #define S_TRAN_LAST_DATA_ACK 0xC8 /* Slave Transmit Last Data ACK */ #define S_RECE_ADDR_ACK 0x60 /* Slave Receive Address ACK */ #define S_RECE_ARB_LOST 0x68 /* Slave Receive Arbitration Lost */ #define S_RECE_DATA_ACK 0x80 /* Slave Receive Data ACK */ #define S_RECE_DATA_NACK 0x88 /* Slave Receive Data NACK */ /* i2c GC Mode Status */ #define GC_ADDR_ACK 0x70 /* GC mode Address ACK */ #define GC_ARB_LOST 0x78 /* GC mode Arbitration Lost */ #define GC_DATA_ACK 0x90 /* GC mode Data ACK */ #define GC_DATA_NACK 0x98 /* GC mode Data NACK */ /* i2c Other Status */ #define ADDR_TRAN_ARB_LOST 0xB0 /* Address Transmit Arbitration Lost */ #define BUS_RELEASED 0xF8 /* Bus Released */ struct i2c_numaker_config { I2C_T *i2c_base; const struct reset_dt_spec reset; uint32_t clk_modidx; uint32_t clk_src; uint32_t clk_div; const struct device *clkctrl_dev; uint32_t irq_n; void (*irq_config_func)(const struct device *dev); const struct pinctrl_dev_config *pincfg; uint32_t bitrate; }; struct i2c_numaker_data { struct k_sem lock; uint32_t dev_config; /* Master transfer context */ struct { struct k_sem xfer_sync; uint16_t addr; struct i2c_msg *msgs_beg; struct i2c_msg *msgs_pos; struct i2c_msg *msgs_end; uint8_t *buf_beg; uint8_t *buf_pos; uint8_t *buf_end; } master_xfer; #ifdef CONFIG_I2C_TARGET /* Slave transfer context */ struct { struct i2c_target_config *slave_config; bool slave_addressed; } slave_xfer; #endif }; /* ACK/NACK last data byte, dependent on whether or not message merge is allowed */ static void m_numaker_i2c_master_xfer_msg_read_last_byte(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; /* Shouldn't invoke with message pointer OOB */ __ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end); /* Should invoke with exactly one data byte remaining for read */ __ASSERT_NO_MSG((data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ); __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1); /* Flags of previous message */ bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP; /* Advance to next messages temporarily */ data->master_xfer.msgs_pos++; /* Has next message? */ if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) { /* Flags of next message */ struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART; /* * Different R/W bit so message merge is disallowed. * Force I2C Repeat Start on I2C Stop/Repeat Start missing */ if (!is_read_next) { if (!do_stop_prev && !do_restart_next) { do_restart_next = true; } } if (do_stop_prev || do_restart_next) { /* NACK last data byte (required for Master Receiver) */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } else { /* ACK last data byte, so to merge adjacent messages into one transaction */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } } else { /* NACK last data byte (required for Master Receiver) */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } /* Roll back message pointer */ data->master_xfer.msgs_pos--; } /* End the transfer, involving I2C Stop and signal to thread */ static void m_numaker_i2c_master_xfer_end(const struct device *dev, bool do_stop) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; if (do_stop) { /* Do I2C Stop */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); } /* Signal master transfer end */ k_sem_give(&data->master_xfer.xfer_sync); } static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev); /* Read next data byte, involving ACK/NACK last data byte and message merge */ static void m_numaker_i2c_master_xfer_msg_read_next_byte(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; switch (data->master_xfer.buf_end - data->master_xfer.buf_pos) { case 0: /* Last data byte ACKed, we'll do message merge */ m_numaker_i2c_master_xfer_msg_end(dev); break; case 1: /* Read last data byte for this message */ m_numaker_i2c_master_xfer_msg_read_last_byte(dev); break; default: /* ACK non-last data byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } } /* End one message transfer, involving message merge and transfer end */ static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; /* Shouldn't invoke with message pointer OOB */ __ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end); /* Should have transferred up */ __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 0); /* Flags of previous message */ bool is_read_prev = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP; /* Advance to next messages */ data->master_xfer.msgs_pos++; /* Has next message? */ if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) { /* Flags of next message */ struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART; /* * Different R/W bit so message merge is disallowed. * Force I2C Repeat Start on I2C Stop/Repeat Start missing */ if (!is_read_prev != !is_read_next) { /* Logical XOR idiom */ if (!do_stop_prev && !do_restart_next) { LOG_WRN("Cannot merge adjacent messages, force I2C Repeat Start"); do_restart_next = true; } } if (do_stop_prev) { /* Do I2C Stop and then Start */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); } else if (do_restart_next) { /* Do I2C Repeat Start */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk); } else { /* Merge into the same transaction */ /* Prepare buffer for current message */ data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf; data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf; data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf + data->master_xfer.msgs_pos->len; if (is_read_prev) { m_numaker_i2c_master_xfer_msg_read_next_byte(dev); } else { /* * Interrupt flag not cleared, expect to re-enter ISR with * context unchanged, except buffer changed for message change. */ } } } else { if (!do_stop_prev) { LOG_WRN("Last message not marked I2C Stop"); } m_numaker_i2c_master_xfer_end(dev, do_stop_prev); } } static int i2c_numaker_configure(const struct device *dev, uint32_t dev_config) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; uint32_t bitrate; /* Check address size */ if (dev_config & I2C_ADDR_10_BITS) { LOG_ERR("10-bits address not supported"); return -ENOTSUP; } switch (I2C_SPEED_GET(dev_config)) { case I2C_SPEED_STANDARD: bitrate = KHZ(100); break; case I2C_SPEED_FAST: bitrate = KHZ(400); break; case I2C_SPEED_FAST_PLUS: bitrate = MHZ(1); break; default: LOG_ERR("Speed code %d not supported", I2C_SPEED_GET(dev_config)); return -ENOTSUP; } I2C_T *i2c_base = config->i2c_base; int err = 0; k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); #ifdef CONFIG_I2C_TARGET if (data->slave_xfer.slave_addressed) { LOG_ERR("Reconfigure with slave being busy"); err = -EBUSY; goto done; } #endif I2C_Open(i2c_base, bitrate); /* INTEN bit and FSM control bits (STA, STO, SI, AA) are packed in one register CTL0. */ i2c_base->CTL0 |= (I2C_CTL0_INTEN_Msk | I2C_CTL0_I2CEN_Msk); data->dev_config = dev_config; done: irq_enable(config->irq_n); k_sem_give(&data->lock); return err; } static int i2c_numaker_get_config(const struct device *dev, uint32_t *dev_config) { struct i2c_numaker_data *data = dev->data; if (!dev_config) { return -EINVAL; } k_sem_take(&data->lock, K_FOREVER); *dev_config = data->dev_config; k_sem_give(&data->lock); return 0; } /* * Master active transfer: * 1. Do I2C Start to start the transfer (thread) * 2. I2C FSM (ISR) * 3. Force I2C Stop to end the transfer (thread) * Slave passive transfer: * 1. Prepare callback (thread) * 2. Do data transfer via above callback (ISR) */ static int i2c_numaker_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; int err = 0; k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); if (data->slave_xfer.slave_addressed) { LOG_ERR("Master transfer with slave being busy"); err = -EBUSY; goto cleanup; } if (num_msgs == 0) { goto cleanup; } /* Prepare to start transfer */ data->master_xfer.addr = addr; data->master_xfer.msgs_beg = msgs; data->master_xfer.msgs_pos = msgs; data->master_xfer.msgs_end = msgs + num_msgs; /* Do I2C Start to start the transfer */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk); irq_enable(config->irq_n); k_sem_take(&data->master_xfer.xfer_sync, K_FOREVER); irq_disable(config->irq_n); /* Check transfer result */ if (data->master_xfer.msgs_pos != data->master_xfer.msgs_end) { bool is_read; bool is_10bit; is_read = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; is_10bit = data->master_xfer.msgs_pos->flags & I2C_MSG_ADDR_10_BITS; LOG_ERR("Failed message:"); LOG_ERR("MSG IDX: %d", data->master_xfer.msgs_pos - data->master_xfer.msgs_beg); LOG_ERR("ADDR (%d-bit): 0x%04X", is_10bit ? 10 : 7, addr); LOG_ERR("DIR: %s", is_read ? "R" : "W"); LOG_ERR("Expected %d bytes transferred, but actual %d", data->master_xfer.msgs_pos->len, data->master_xfer.buf_pos - data->master_xfer.buf_beg); err = -EIO; goto i2c_stop; } i2c_stop: /* Do I2C Stop to release bus ownership */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); #ifdef CONFIG_I2C_TARGET /* Enable slave mode if one slave is registered */ if (data->slave_xfer.slave_config) { I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } #endif cleanup: irq_enable(config->irq_n); k_sem_give(&data->lock); return err; } #ifdef CONFIG_I2C_TARGET static int i2c_numaker_slave_register(const struct device *dev, struct i2c_target_config *slave_config) { if (!slave_config || !slave_config->callbacks) { return -EINVAL; } if (slave_config->flags & I2C_ADDR_10_BITS) { LOG_ERR("10-bits address not supported"); return -ENOTSUP; } const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; int err = 0; k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); if (data->slave_xfer.slave_config) { err = -EBUSY; goto cleanup; } data->slave_xfer.slave_config = slave_config; /* Slave address */ I2C_SetSlaveAddr(i2c_base, 0, slave_config->address, I2C_GCMODE_DISABLE); /* Slave address state */ data->slave_xfer.slave_addressed = false; /* Enable slave mode */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); cleanup: irq_enable(config->irq_n); k_sem_give(&data->lock); return err; } static int i2c_numaker_slave_unregister(const struct device *dev, struct i2c_target_config *slave_config) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; int err = 0; if (!slave_config) { return -EINVAL; } k_sem_take(&data->lock, K_FOREVER); irq_disable(config->irq_n); if (data->slave_xfer.slave_config != slave_config) { err = -EINVAL; goto cleanup; } if (data->slave_xfer.slave_addressed) { LOG_ERR("Unregister slave driver with slave being busy"); err = -EBUSY; goto cleanup; } /* Slave address: Zero */ I2C_SetSlaveAddr(i2c_base, 0, 0, I2C_GCMODE_DISABLE); /* Slave address state */ data->slave_xfer.slave_addressed = false; /* Disable slave mode */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); data->slave_xfer.slave_config = NULL; cleanup: irq_enable(config->irq_n); k_sem_give(&data->lock); return err; } #endif static int i2c_numaker_recover_bus(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; k_sem_take(&data->lock, K_FOREVER); /* Do I2C Stop to release bus ownership */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk); k_sem_give(&data->lock); return 0; } static void i2c_numaker_isr(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; I2C_T *i2c_base = config->i2c_base; #ifdef CONFIG_I2C_TARGET struct i2c_target_config *slave_config = data->slave_xfer.slave_config; const struct i2c_target_callbacks *slave_callbacks = slave_config ? slave_config->callbacks : NULL; uint8_t data_byte; #endif uint32_t status; if (I2C_GET_TIMEOUT_FLAG(i2c_base)) { I2C_ClearTimeoutFlag(i2c_base); return; } status = I2C_GET_STATUS(i2c_base); switch (status) { case M_START: /* Start */ case M_REPEAT_START: /* Master Repeat Start */ /* Prepare buffer for current message */ data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf; data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf; data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf + data->master_xfer.msgs_pos->len; /* Write I2C address */ struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos; bool is_read = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; uint16_t addr = data->master_xfer.addr; int addr_rw = is_read ? ((addr << 1) | 1) : (addr << 1); I2C_SET_DATA(i2c_base, (uint8_t) (addr_rw & 0xFF)); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); break; case M_TRAN_ADDR_ACK: /* Master Transmit Address ACK */ case M_TRAN_DATA_ACK: /* Master Transmit Data ACK */ __ASSERT_NO_MSG(data->master_xfer.buf_pos); if (data->master_xfer.buf_pos < data->master_xfer.buf_end) { I2C_SET_DATA(i2c_base, *data->master_xfer.buf_pos++); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* End this message */ m_numaker_i2c_master_xfer_msg_end(dev); } break; case M_TRAN_ADDR_NACK: /* Master Transmit Address NACK */ case M_TRAN_DATA_NACK: /* Master Transmit Data NACK */ case M_RECE_ADDR_NACK: /* Master Receive Address NACK */ case M_ARB_LOST: /* Master Arbitration Lost */ m_numaker_i2c_master_xfer_end(dev, true); break; case M_RECE_ADDR_ACK: /* Master Receive Address ACK */ case M_RECE_DATA_ACK: /* Master Receive Data ACK */ __ASSERT_NO_MSG(data->master_xfer.buf_pos); if (status == M_RECE_ADDR_ACK) { __ASSERT_NO_MSG(data->master_xfer.buf_pos < data->master_xfer.buf_end); } else if (status == M_RECE_DATA_ACK) { __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) >= 1); *data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); } m_numaker_i2c_master_xfer_msg_read_next_byte(dev); break; case M_RECE_DATA_NACK: /* Master Receive Data NACK */ __ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1); *data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base); /* End this message */ m_numaker_i2c_master_xfer_msg_end(dev); break; case BUS_ERROR: /* Bus error */ m_numaker_i2c_master_xfer_end(dev, true); break; #ifdef CONFIG_I2C_TARGET /* NOTE: Don't disable interrupt here because slave mode relies on */ /* for passive transfer in ISR. */ /* Slave Transmit */ case S_TRAN_ADDR_ACK: /* Slave Transmit Address ACK */ case ADDR_TRAN_ARB_LOST: /* Slave Transmit Arbitration Lost */ data->slave_xfer.slave_addressed = true; if (slave_callbacks->read_requested(slave_config, &data_byte) == 0) { /* Non-last data byte */ I2C_SET_DATA(i2c_base, data_byte); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* Go S_TRAN_LAST_DATA_ACK on error */ I2C_SET_DATA(i2c_base, 0xFF); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; case S_TRAN_DATA_ACK: /* Slave Transmit Data ACK */ if (slave_callbacks->read_processed(slave_config, &data_byte) == 0) { /* Non-last data byte */ I2C_SET_DATA(i2c_base, data_byte); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* Go S_TRAN_LAST_DATA_ACK on error */ I2C_SET_DATA(i2c_base, 0xFF); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; case S_TRAN_DATA_NACK: /* Slave Transmit Data NACK */ case S_TRAN_LAST_DATA_ACK: /* Slave Transmit Last Data ACK */ /* Go slave end */ data->slave_xfer.slave_addressed = false; slave_callbacks->stop(slave_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; /* Slave Receive */ case S_RECE_DATA_ACK: /* Slave Receive Data ACK */ data_byte = I2C_GET_DATA(i2c_base); if (slave_callbacks->write_received(slave_config, data_byte) == 0) { /* Write OK, ACK next data byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* Write FAILED, NACK next data byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; case S_RECE_DATA_NACK: /* Slave Receive Data NACK */ /* Go slave end */ data->slave_xfer.slave_addressed = false; slave_callbacks->stop(slave_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; case S_RECE_ADDR_ACK: /* Slave Receive Address ACK */ case S_RECE_ARB_LOST: /* Slave Receive Arbitration Lost */ data->slave_xfer.slave_addressed = true; if (slave_callbacks->write_requested(slave_config) == 0) { /* Write ready, ACK next byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); } else { /* Write not ready, NACK next byte */ I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk); } break; case S_REPEAT_START_STOP: /* Slave Transmit/Receive Repeat Start or Stop */ /* Go slave end */ data->slave_xfer.slave_addressed = false; slave_callbacks->stop(slave_config); I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk); break; #endif /* CONFIG_I2C_TARGET */ case BUS_RELEASED: /* Bus Released */ /* Ignore the interrupt raised by BUS_RELEASED. */ break; default: __ASSERT(false, "Uncaught I2C FSM state"); m_numaker_i2c_master_xfer_end(dev, true); } } static int i2c_numaker_init(const struct device *dev) { const struct i2c_numaker_config *config = dev->config; struct i2c_numaker_data *data = dev->data; int err = 0; struct numaker_scc_subsys scc_subsys; /* Validate this module's reset object */ if (!device_is_ready(config->reset.dev)) { LOG_ERR("reset controller not ready"); return -ENODEV; } /* Clean mutable context */ memset(data, 0x00, sizeof(*data)); k_sem_init(&data->lock, 1, 1); k_sem_init(&data->master_xfer.xfer_sync, 0, 1); SYS_UnlockReg(); memset(&scc_subsys, 0x00, sizeof(scc_subsys)); scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC; scc_subsys.pcc.clk_modidx = config->clk_modidx; scc_subsys.pcc.clk_src = config->clk_src; scc_subsys.pcc.clk_div = config->clk_div; /* Equivalent to CLK_EnableModuleClock() */ err = clock_control_on(config->clkctrl_dev, (clock_control_subsys_t) &scc_subsys); if (err != 0) { goto cleanup; } /* Equivalent to CLK_SetModuleClock() */ err = clock_control_configure(config->clkctrl_dev, (clock_control_subsys_t) &scc_subsys, NULL); if (err != 0) { goto cleanup; } /* Configure pinmux (NuMaker's SYS MFP) */ err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); if (err != 0) { goto cleanup; } /* Reset I2C to default state, same as BSP's SYS_ResetModule(id_rst) */ reset_line_toggle_dt(&config->reset); err = i2c_numaker_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate)); if (err != 0) { goto cleanup; } config->irq_config_func(dev); cleanup: SYS_LockReg(); return err; } static DEVICE_API(i2c, i2c_numaker_driver_api) = { .configure = i2c_numaker_configure, .get_config = i2c_numaker_get_config, .transfer = i2c_numaker_transfer, #ifdef CONFIG_I2C_TARGET .target_register = i2c_numaker_slave_register, .target_unregister = i2c_numaker_slave_unregister, #endif #ifdef CONFIG_I2C_RTIO .iodev_submit = i2c_iodev_submit_fallback, #endif .recover_bus = i2c_numaker_recover_bus, }; #define I2C_NUMAKER_INIT(inst) \ PINCTRL_DT_INST_DEFINE(inst); \ \ static void i2c_numaker_irq_config_func_##inst(const struct device *dev) \ { \ IRQ_CONNECT(DT_INST_IRQN(inst), \ DT_INST_IRQ(inst, priority), \ i2c_numaker_isr, \ DEVICE_DT_INST_GET(inst), \ 0); \ \ irq_enable(DT_INST_IRQN(inst)); \ } \ \ static const struct i2c_numaker_config i2c_numaker_config_##inst = { \ .i2c_base = (I2C_T *) DT_INST_REG_ADDR(inst), \ .reset = RESET_DT_SPEC_INST_GET(inst), \ .clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \ .clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \ .clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \ .clkctrl_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))),\ .irq_n = DT_INST_IRQN(inst), \ .irq_config_func = i2c_numaker_irq_config_func_##inst, \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ .bitrate = DT_INST_PROP(inst, clock_frequency), \ }; \ \ static struct i2c_numaker_data i2c_numaker_data_##inst; \ \ I2C_DEVICE_DT_INST_DEFINE(inst, \ i2c_numaker_init, \ NULL, \ &i2c_numaker_data_##inst, \ &i2c_numaker_config_##inst, \ POST_KERNEL, \ CONFIG_I2C_INIT_PRIORITY, \ &i2c_numaker_driver_api); DT_INST_FOREACH_STATUS_OKAY(I2C_NUMAKER_INIT);