Skip to content

Commit

Permalink
drivers: i2c: Add support for Apollo3 SoCs I2C
Browse files Browse the repository at this point in the history
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 <hluo@ambiq.com>
  • Loading branch information
AlessandroLuo authored and nashif committed May 22, 2024
1 parent 95dde52 commit c8ae265
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 27 deletions.
8 changes: 8 additions & 0 deletions boards/ambiq/apollo3_evb/apollo3_evb.dts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@
status = "okay";
};

&i2c3 {
compatible = "ambiq,i2c";
pinctrl-0 = <&i2c3_default>;
pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>;
status = "okay";
};

&gpio0_31 {
status = "okay";
};
Expand Down
1 change: 1 addition & 0 deletions boards/ambiq/apollo3_evb/apollo3_evb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ supported:
- uart
- watchdog
- gpio
- i2c
testing:
ignore_tags:
- net
Expand Down
8 changes: 8 additions & 0 deletions boards/ambiq/apollo3p_evb/apollo3p_evb.dts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@
status = "okay";
};

&i2c3 {
compatible = "ambiq,i2c";
pinctrl-0 = <&i2c3_default>;
pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>;
status = "okay";
};

&gpio0_31 {
status = "okay";
};
Expand Down
1 change: 1 addition & 0 deletions boards/ambiq/apollo3p_evb/apollo3p_evb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ supported:
- uart
- watchdog
- gpio
- i2c
testing:
ignore_tags:
- net
Expand Down
11 changes: 11 additions & 0 deletions drivers/i2c/Kconfig.ambiq
Original file line number Diff line number Diff line change
Expand Up @@ -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
142 changes: 127 additions & 15 deletions drivers/i2c/i2c_ambiq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -96,20 +166,29 @@ 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;
}

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);
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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, \
Expand Down
12 changes: 6 additions & 6 deletions dts/arm/ambiq/ambiq_apollo3_blue.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
};

iom0: iom@50004000 {
i2c0: i2c@50004000 {
reg = <0x50004000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -104,7 +104,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x2>;
};

iom1: iom@50005000 {
i2c1: i2c@50005000 {
reg = <0x50005000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -113,7 +113,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x4>;
};

iom2: iom@50006000 {
i2c2: i2c@50006000 {
reg = <0x50006000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -122,7 +122,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x8>;
};

iom3: iom@50007000 {
i2c3: i2c@50007000 {
reg = <0x50007000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -131,7 +131,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x10>;
};

iom4: iom@50008000 {
i2c4: i2c@50008000 {
reg = <0x50008000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -140,7 +140,7 @@
ambiq,pwrcfg = <&pwrcfg 0x8 0x20>;
};

iom5: iom@50009000 {
i2c5: i2c@50009000 {
reg = <0x50009000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
Expand Down
Loading

0 comments on commit c8ae265

Please sign in to comment.