From c8ae26549d0cdb2fbc98c45d885f1516837ebf09 Mon Sep 17 00:00:00 2001 From: Hao Luo Date: Wed, 10 Jan 2024 15:15:29 +0800 Subject: [PATCH] drivers: i2c: Add support for Apollo3 SoCs I2C This commit adds support for the I2C which can be found in Apollo3 SoCs, it can work in both DMA and non-DMA modes Signed-off-by: Hao Luo --- boards/ambiq/apollo3_evb/apollo3_evb.dts | 8 ++ boards/ambiq/apollo3_evb/apollo3_evb.yaml | 1 + boards/ambiq/apollo3p_evb/apollo3p_evb.dts | 8 ++ boards/ambiq/apollo3p_evb/apollo3p_evb.yaml | 1 + drivers/i2c/Kconfig.ambiq | 11 ++ drivers/i2c/i2c_ambiq.c | 142 +++++++++++++++++--- dts/arm/ambiq/ambiq_apollo3_blue.dtsi | 12 +- dts/arm/ambiq/ambiq_apollo3p_blue.dtsi | 12 +- 8 files changed, 168 insertions(+), 27 deletions(-) diff --git a/boards/ambiq/apollo3_evb/apollo3_evb.dts b/boards/ambiq/apollo3_evb/apollo3_evb.dts index fd961a550bef..9f1cac0b3098 100644 --- a/boards/ambiq/apollo3_evb/apollo3_evb.dts +++ b/boards/ambiq/apollo3_evb/apollo3_evb.dts @@ -95,6 +95,14 @@ status = "okay"; }; +&i2c3 { + compatible = "ambiq,i2c"; + pinctrl-0 = <&i2c3_default>; + pinctrl-names = "default"; + clock-frequency = ; + status = "okay"; +}; + &gpio0_31 { status = "okay"; }; diff --git a/boards/ambiq/apollo3_evb/apollo3_evb.yaml b/boards/ambiq/apollo3_evb/apollo3_evb.yaml index a85086461c3e..f55c135f1d4a 100644 --- a/boards/ambiq/apollo3_evb/apollo3_evb.yaml +++ b/boards/ambiq/apollo3_evb/apollo3_evb.yaml @@ -11,6 +11,7 @@ supported: - uart - watchdog - gpio + - i2c testing: ignore_tags: - net diff --git a/boards/ambiq/apollo3p_evb/apollo3p_evb.dts b/boards/ambiq/apollo3p_evb/apollo3p_evb.dts index 2bccb1746d84..02acc1bed570 100644 --- a/boards/ambiq/apollo3p_evb/apollo3p_evb.dts +++ b/boards/ambiq/apollo3p_evb/apollo3p_evb.dts @@ -95,6 +95,14 @@ status = "okay"; }; +&i2c3 { + compatible = "ambiq,i2c"; + pinctrl-0 = <&i2c3_default>; + pinctrl-names = "default"; + clock-frequency = ; + status = "okay"; +}; + &gpio0_31 { status = "okay"; }; diff --git a/boards/ambiq/apollo3p_evb/apollo3p_evb.yaml b/boards/ambiq/apollo3p_evb/apollo3p_evb.yaml index d789a2519bcc..0ed3fe2929df 100644 --- a/boards/ambiq/apollo3p_evb/apollo3p_evb.yaml +++ b/boards/ambiq/apollo3p_evb/apollo3p_evb.yaml @@ -11,6 +11,7 @@ supported: - uart - watchdog - gpio + - i2c testing: ignore_tags: - net diff --git a/drivers/i2c/Kconfig.ambiq b/drivers/i2c/Kconfig.ambiq index 040a93866e97..04a80848d9f4 100644 --- a/drivers/i2c/Kconfig.ambiq +++ b/drivers/i2c/Kconfig.ambiq @@ -13,3 +13,14 @@ config I2C_AMBIQ select AMBIQ_HAL_USE_I2C help Enable driver for Ambiq I2C. + +config I2C_AMBIQ_DMA + bool "AMBIQ APOLLO I2C DMA Support" + help + Enable DMA for Ambiq I2C. + +config I2C_DMA_TCB_BUFFER_SIZE + int "DMA Transfer Control Buffer size in words." + default 1024 + help + DMA Transfer Control Buffer size in words diff --git a/drivers/i2c/i2c_ambiq.c b/drivers/i2c/i2c_ambiq.c index e8b62fb2c45a..f3c5b90e9003 100644 --- a/drivers/i2c/i2c_ambiq.c +++ b/drivers/i2c/i2c_ambiq.c @@ -19,7 +19,8 @@ LOG_MODULE_REGISTER(ambiq_i2c, CONFIG_I2C_LOG_LEVEL); typedef int (*ambiq_i2c_pwr_func_t)(void); -#define PWRCTRL_MAX_WAIT_US 5 +#define PWRCTRL_MAX_WAIT_US 5 +#define I2C_TRANSFER_TIMEOUT_MSEC 500 /* Transfer timeout period */ #include "i2c-priv.h" @@ -29,13 +30,50 @@ struct i2c_ambiq_config { uint32_t bitrate; const struct pinctrl_dev_config *pcfg; ambiq_i2c_pwr_func_t pwr_func; + void (*irq_config_func)(void); }; +typedef void (*i2c_ambiq_callback_t)(const struct device *dev, int result, void *data); + struct i2c_ambiq_data { am_hal_iom_config_t iom_cfg; - void *IOMHandle; + void *iom_handler; + struct k_sem bus_sem; + struct k_sem transfer_sem; + i2c_ambiq_callback_t callback; + void *callback_data; + int inst_idx; + uint32_t transfer_status; }; +#ifdef CONFIG_I2C_AMBIQ_DMA +static __aligned(32) struct { + __aligned(32) uint32_t buf[CONFIG_I2C_DMA_TCB_BUFFER_SIZE]; +} i2c_dma_tcb_buf[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)] __attribute__((__section__(".nocache"))); + +static void i2c_ambiq_callback(void *callback_ctxt, uint32_t status) +{ + const struct device *dev = callback_ctxt; + struct i2c_ambiq_data *data = dev->data; + + if (data->callback) { + data->callback(dev, status, data->callback_data); + } + data->transfer_status = status; + k_sem_give(&data->transfer_sem); +} +#endif + +static void i2c_ambiq_isr(const struct device *dev) +{ + uint32_t ui32Status; + struct i2c_ambiq_data *data = dev->data; + + am_hal_iom_interrupt_status_get(data->iom_handler, false, &ui32Status); + am_hal_iom_interrupt_clear(data->iom_handler, ui32Status); + am_hal_iom_interrupt_service(data->iom_handler, ui32Status); +} + static int i2c_ambiq_read(const struct device *dev, struct i2c_msg *msg, uint16_t addr) { struct i2c_ambiq_data *data = dev->data; @@ -50,9 +88,24 @@ static int i2c_ambiq_read(const struct device *dev, struct i2c_msg *msg, uint16_ trans.ui32NumBytes = msg->len; trans.pui32RxBuffer = (uint32_t *)msg->buf; - ret = am_hal_iom_blocking_transfer(data->IOMHandle, &trans); - - return ret; +#ifdef CONFIG_I2C_AMBIQ_DMA + data->transfer_status = -EFAULT; + ret = am_hal_iom_nonblocking_transfer(data->iom_handler, &trans, i2c_ambiq_callback, + (void *)dev); + if (k_sem_take(&data->transfer_sem, K_MSEC(I2C_TRANSFER_TIMEOUT_MSEC))) { + LOG_ERR("Timeout waiting for transfer complete"); + /* cancel timed out transaction */ + am_hal_iom_disable(data->iom_handler); + /* clean up for next xfer */ + k_sem_reset(&data->transfer_sem); + am_hal_iom_enable(data->iom_handler); + return -ETIMEDOUT; + } + ret = data->transfer_status; +#else + ret = am_hal_iom_blocking_transfer(data->iom_handler, &trans); +#endif + return (ret != AM_HAL_STATUS_SUCCESS) ? -EIO : 0; } static int i2c_ambiq_write(const struct device *dev, struct i2c_msg *msg, uint16_t addr) @@ -69,9 +122,26 @@ static int i2c_ambiq_write(const struct device *dev, struct i2c_msg *msg, uint16 trans.ui32NumBytes = msg->len; trans.pui32TxBuffer = (uint32_t *)msg->buf; - ret = am_hal_iom_blocking_transfer(data->IOMHandle, &trans); +#ifdef CONFIG_I2C_AMBIQ_DMA + data->transfer_status = -EFAULT; + ret = am_hal_iom_nonblocking_transfer(data->iom_handler, &trans, i2c_ambiq_callback, + (void *)dev); + + if (k_sem_take(&data->transfer_sem, K_MSEC(I2C_TRANSFER_TIMEOUT_MSEC))) { + LOG_ERR("Timeout waiting for transfer complete"); + /* cancel timed out transaction */ + am_hal_iom_disable(data->iom_handler); + /* clean up for next xfer */ + k_sem_reset(&data->transfer_sem); + am_hal_iom_enable(data->iom_handler); + return -ETIMEDOUT; + } + ret = data->transfer_status; +#else + ret = am_hal_iom_blocking_transfer(data->iom_handler, &trans); +#endif - return ret; + return (ret != AM_HAL_STATUS_SUCCESS) ? -EIO : 0; } static int i2c_ambiq_configure(const struct device *dev, uint32_t dev_config) @@ -96,7 +166,12 @@ static int i2c_ambiq_configure(const struct device *dev, uint32_t dev_config) return -EINVAL; } - am_hal_iom_configure(data->IOMHandle, &data->iom_cfg); +#ifdef CONFIG_I2C_AMBIQ_DMA + data->iom_cfg.pNBTxnBuf = i2c_dma_tcb_buf[data->inst_idx].buf; + data->iom_cfg.ui32NBTxnBufLength = CONFIG_I2C_DMA_TCB_BUFFER_SIZE; +#endif + + am_hal_iom_configure(data->iom_handler, &data->iom_cfg); return 0; } @@ -104,12 +179,16 @@ static int i2c_ambiq_configure(const struct device *dev, uint32_t dev_config) static int i2c_ambiq_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { + struct i2c_ambiq_data *data = dev->data; int ret = 0; if (!num_msgs) { return 0; } + /* Send out messages */ + k_sem_take(&data->bus_sem, K_FOREVER); + for (int i = 0; i < num_msgs; i++) { if (msgs[i].flags & I2C_MSG_READ) { ret = i2c_ambiq_read(dev, &(msgs[i]), addr); @@ -122,6 +201,8 @@ static int i2c_ambiq_transfer(const struct device *dev, struct i2c_msg *msgs, ui } } + k_sem_give(&data->bus_sem); + return 0; } @@ -134,21 +215,41 @@ static int i2c_ambiq_init(const struct device *dev) data->iom_cfg.eInterfaceMode = AM_HAL_IOM_I2C_MODE; - ret = am_hal_iom_initialize((config->base - REG_IOM_BASEADDR) / config->size, - &data->IOMHandle); + if (AM_HAL_STATUS_SUCCESS != + am_hal_iom_initialize((config->base - REG_IOM_BASEADDR) / config->size, + &data->iom_handler)) { + LOG_ERR("Fail to initialize I2C\n"); + return -ENXIO; + } ret = config->pwr_func(); - ret = i2c_ambiq_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); + ret |= i2c_ambiq_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); + if (ret < 0) { + LOG_ERR("Fail to config I2C\n"); + goto end; + } ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); - if (ret < 0) { - return ret; + LOG_ERR("Fail to config I2C pins\n"); + goto end; } - ret = am_hal_iom_enable(data->IOMHandle); +#ifdef CONFIG_I2C_AMBIQ_DMA + am_hal_iom_interrupt_clear(data->iom_handler, AM_HAL_IOM_INT_CQUPD | AM_HAL_IOM_INT_ERR); + am_hal_iom_interrupt_enable(data->iom_handler, AM_HAL_IOM_INT_CQUPD | AM_HAL_IOM_INT_ERR); + config->irq_config_func(); +#endif + if (AM_HAL_STATUS_SUCCESS != am_hal_iom_enable(data->iom_handler)) { + LOG_ERR("Fail to enable I2C\n"); + ret = -EIO; + } +end: + if (ret < 0) { + am_hal_iom_uninitialize(data->iom_handler); + } return ret; } @@ -167,12 +268,23 @@ static const struct i2c_driver_api i2c_ambiq_driver_api = { k_busy_wait(PWRCTRL_MAX_WAIT_US); \ return 0; \ } \ - static struct i2c_ambiq_data i2c_ambiq_data##n; \ + static void i2c_irq_config_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), i2c_ambiq_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + }; \ + static struct i2c_ambiq_data i2c_ambiq_data##n = { \ + .bus_sem = Z_SEM_INITIALIZER(i2c_ambiq_data##n.bus_sem, 1, 1), \ + .transfer_sem = Z_SEM_INITIALIZER(i2c_ambiq_data##n.transfer_sem, 0, 1), \ + .inst_idx = n, \ + }; \ static const struct i2c_ambiq_config i2c_ambiq_config##n = { \ .base = DT_INST_REG_ADDR(n), \ .size = DT_INST_REG_SIZE(n), \ .bitrate = DT_INST_PROP(n, clock_frequency), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .irq_config_func = i2c_irq_config_func_##n, \ .pwr_func = pwr_on_ambiq_i2c_##n}; \ I2C_DEVICE_DT_INST_DEFINE(n, i2c_ambiq_init, NULL, &i2c_ambiq_data##n, \ &i2c_ambiq_config##n, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ diff --git a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi index 5ae40c06790a..0005102b6972 100644 --- a/dts/arm/ambiq/ambiq_apollo3_blue.dtsi +++ b/dts/arm/ambiq/ambiq_apollo3_blue.dtsi @@ -95,7 +95,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x100>; }; - iom0: iom@50004000 { + i2c0: i2c@50004000 { reg = <0x50004000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -104,7 +104,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x2>; }; - iom1: iom@50005000 { + i2c1: i2c@50005000 { reg = <0x50005000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -113,7 +113,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x4>; }; - iom2: iom@50006000 { + i2c2: i2c@50006000 { reg = <0x50006000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -122,7 +122,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x8>; }; - iom3: iom@50007000 { + i2c3: i2c@50007000 { reg = <0x50007000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -131,7 +131,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x10>; }; - iom4: iom@50008000 { + i2c4: i2c@50008000 { reg = <0x50008000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -140,7 +140,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x20>; }; - iom5: iom@50009000 { + i2c5: i2c@50009000 { reg = <0x50009000 0x1000>; #address-cells = <1>; #size-cells = <0>; diff --git a/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi b/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi index ac53b55883e2..dc578356bf14 100644 --- a/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi +++ b/dts/arm/ambiq/ambiq_apollo3p_blue.dtsi @@ -95,7 +95,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x100>; }; - iom0: iom@50004000 { + i2c0: i2c@50004000 { reg = <0x50004000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -104,7 +104,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x2>; }; - iom1: iom@50005000 { + i2c1: i2c@50005000 { reg = <0x50005000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -113,7 +113,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x4>; }; - iom2: iom@50006000 { + i2c2: i2c@50006000 { reg = <0x50006000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -122,7 +122,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x8>; }; - iom3: iom@50007000 { + i2c3: i2c@50007000 { reg = <0x50007000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -131,7 +131,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x10>; }; - iom4: iom@50008000 { + i2c4: i2c@50008000 { reg = <0x50008000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -140,7 +140,7 @@ ambiq,pwrcfg = <&pwrcfg 0x8 0x20>; }; - iom5: iom@50009000 { + i2c5: i2c@50009000 { reg = <0x50009000 0x1000>; #address-cells = <1>; #size-cells = <0>;