diff --git a/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.defconfig.mcimx7_m4 b/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.defconfig.mcimx7_m4 index bacdf8bf43405..5e30bcde2d8d8 100644 --- a/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.defconfig.mcimx7_m4 +++ b/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.defconfig.mcimx7_m4 @@ -43,6 +43,13 @@ config UART_IMX endif # SERIAL +if I2C + +config I2C_IMX + def_bool y + +endif # I2C + config DOMAIN_ID int default 1 diff --git a/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.soc b/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.soc index 0515d2fa3d168..d3658e7f2d291 100644 --- a/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.soc +++ b/arch/arm/soc/nxp_imx/mcimx7_m4/Kconfig.soc @@ -13,6 +13,7 @@ config SOC_MCIMX7_M4 bool "SOC_MCIMX7_M4" select HAS_IMX_HAL select HAS_IMX_GPIO + select HAS_IMX_I2C endchoice diff --git a/arch/arm/soc/nxp_imx/mcimx7_m4/soc.c b/arch/arm/soc/nxp_imx/mcimx7_m4/soc.c index 26fdbb0fe7827..71cc30e707fca 100644 --- a/arch/arm/soc/nxp_imx/mcimx7_m4/soc.c +++ b/arch/arm/soc/nxp_imx/mcimx7_m4/soc.c @@ -99,6 +99,62 @@ static void nxp_mcimx7_uart_config(void) } #endif /* CONFIG_UART_IMX */ + +#ifdef CONFIG_I2C_IMX +static void nxp_mcimx7_i2c_config(void) +{ + +#ifdef CONFIG_I2C_1 + /* In this example, we need to grasp board I2C exclusively */ + RDC_SetPdapAccess(RDC, rdcPdapI2c1, + RDC_DOMAIN_PERM(CONFIG_DOMAIN_ID, RDC_DOMAIN_PERM_RW), + false, false); + /* Select I2C clock derived from OSC clock(24M) */ + CCM_UpdateRoot(CCM, ccmRootI2c1, ccmRootmuxI2cOsc24m, 0, 0); + /* Enable I2C clock */ + CCM_EnableRoot(CCM, ccmRootI2c1); + CCM_ControlGate(CCM, ccmCcgrGateI2c1, ccmClockNeededRunWait); +#endif /* CONFIG_I2C_1 */ + +#ifdef CONFIG_I2C_2 + /* In this example, we need to grasp board I2C exclusively */ + RDC_SetPdapAccess(RDC, rdcPdapI2c2, + RDC_DOMAIN_PERM(CONFIG_DOMAIN_ID, RDC_DOMAIN_PERM_RW), + false, false); + /* Select I2C clock derived from OSC clock(24M) */ + CCM_UpdateRoot(CCM, ccmRootI2c2, ccmRootmuxI2cOsc24m, 0, 0); + /* Enable I2C clock */ + CCM_EnableRoot(CCM, ccmRootI2c2); + CCM_ControlGate(CCM, ccmCcgrGateI2c2, ccmClockNeededRunWait); +#endif /* CONFIG_I2C_2 */ + +#ifdef CONFIG_I2C_3 + /* In this example, we need to grasp board I2C exclusively */ + RDC_SetPdapAccess(RDC, rdcPdapI2c3, + RDC_DOMAIN_PERM(CONFIG_DOMAIN_ID, RDC_DOMAIN_PERM_RW), + false, false); + /* Select I2C clock derived from OSC clock(24M) */ + CCM_UpdateRoot(CCM, ccmRootI2c3, ccmRootmuxI2cOsc24m, 0, 0); + /* Enable I2C clock */ + CCM_EnableRoot(CCM, ccmRootI2c3); + CCM_ControlGate(CCM, ccmCcgrGateI2c3, ccmClockNeededRunWait); +#endif /* CONFIG_I2C_3 */ + +#ifdef CONFIG_I2C_4 + /* In this example, we need to grasp board I2C exclusively */ + RDC_SetPdapAccess(RDC, rdcPdapI2c4, + RDC_DOMAIN_PERM(CONFIG_DOMAIN_ID, RDC_DOMAIN_PERM_RW), + false, false); + /* Select I2C clock derived from OSC clock(24M) */ + CCM_UpdateRoot(CCM, ccmRootI2c4, ccmRootmuxI2cOsc24m, 0, 0); + /* Enable I2C clock */ + CCM_EnableRoot(CCM, ccmRootI2c4); + CCM_ControlGate(CCM, ccmCcgrGateI2c4, ccmClockNeededRunWait); +#endif /* CONFIG_I2C_4 */ + +} +#endif /* CONFIG_I2C_IMX */ + static int nxp_mcimx7_init(struct device *arg) { ARG_UNUSED(arg); @@ -117,6 +173,10 @@ static int nxp_mcimx7_init(struct device *arg) nxp_mcimx7_uart_config(); #endif /* CONFIG_UART_IMX */ +#ifdef CONFIG_I2C_IMX + nxp_mcimx7_i2c_config(); +#endif /* CONFIG_I2C_IMX */ + return 0; } diff --git a/boards/arm/colibri_imx7d_m4/Kconfig.board b/boards/arm/colibri_imx7d_m4/Kconfig.board index f3a2cc76737e7..17485d1c571ee 100644 --- a/boards/arm/colibri_imx7d_m4/Kconfig.board +++ b/boards/arm/colibri_imx7d_m4/Kconfig.board @@ -9,3 +9,4 @@ config BOARD_COLIBRI_IMX7D_M4 bool "Toradex Colibri iMX7 Dual" depends on SOC_SERIES_IMX7_M4 select SOC_PART_NUMBER_MCIMX7D5EVM10SC + select HAS_DTS_I2C_DEVICE diff --git a/boards/arm/colibri_imx7d_m4/Kconfig.defconfig b/boards/arm/colibri_imx7d_m4/Kconfig.defconfig index 2298497fca2b6..28441eed6d112 100644 --- a/boards/arm/colibri_imx7d_m4/Kconfig.defconfig +++ b/boards/arm/colibri_imx7d_m4/Kconfig.defconfig @@ -42,4 +42,21 @@ config UART_IMX_UART_2 endif # UART_IMX +if I2C_IMX + +config I2C_1 + def_bool n + +config I2C_2 + def_bool n + +config I2C_3 + def_bool n + +config I2C_4 + def_bool y + +endif # I2C_IMX + + endif # BOARD_COLIBRI_IMX7D_M4 diff --git a/boards/arm/colibri_imx7d_m4/colibri_imx7d_m4.dts b/boards/arm/colibri_imx7d_m4/colibri_imx7d_m4.dts index c758fabd8422e..a69ad72dc34ba 100644 --- a/boards/arm/colibri_imx7d_m4/colibri_imx7d_m4.dts +++ b/boards/arm/colibri_imx7d_m4/colibri_imx7d_m4.dts @@ -18,6 +18,7 @@ uart-2 = &uart2; led0 = &green_led; sw0 = &user_switch_1; + i2c-4 = &i2c4; }; chosen { @@ -58,3 +59,7 @@ &gpio2 { status = "ok"; }; + +&i2c4 { + status = "ok"; +}; diff --git a/boards/arm/colibri_imx7d_m4/doc/colibri_imx7d_m4.rst b/boards/arm/colibri_imx7d_m4/doc/colibri_imx7d_m4.rst index 1f996607e9311..4d2c96e10ef25 100644 --- a/boards/arm/colibri_imx7d_m4/doc/colibri_imx7d_m4.rst +++ b/boards/arm/colibri_imx7d_m4/doc/colibri_imx7d_m4.rst @@ -82,6 +82,8 @@ supports the following hardware features on the Cortex M4 Core: +-----------+------------+-------------------------------------+ | GPIO | on-chip | gpio | +-----------+------------+-------------------------------------+ +| I2C | on-chip | i2c | ++-----------+------------+-------------------------------------+ | UART | on-chip | serial port-polling; | | | | serial port-interrupt | +-----------+------------+-------------------------------------+ @@ -109,6 +111,10 @@ was tested with the following pinmux controller configuration. +---------------+-----------------+---------------------------+ | SODIMM_133 | GPIO2_IO26 | SW0 | +---------------+-----------------+---------------------------+ +| SODIMM_194 | I2C4_SDA | I2C_SDA | ++---------------+-----------------+---------------------------+ +| SODIMM_196 | I2C4_SCL | I2C_SCL | ++---------------+-----------------+---------------------------+ System Clock ============ diff --git a/boards/arm/colibri_imx7d_m4/pinmux.c b/boards/arm/colibri_imx7d_m4/pinmux.c index bb5ac01404450..0bcf8342dd985 100644 --- a/boards/arm/colibri_imx7d_m4/pinmux.c +++ b/boards/arm/colibri_imx7d_m4/pinmux.c @@ -48,6 +48,102 @@ static int colibri_imx7d_m4_pinmux_init(struct device *dev) IOMUXC_UART2_RX_DATA_SELECT_INPUT_DAISY(3); #endif /* CONFIG_UART_IMX_UART_2 */ +#ifdef CONFIG_I2C_1 + IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL = + IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C1_SCL_SION_MASK; + IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA = + IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C1_SDA_SION_MASK; + + IOMUXC_I2C1_SCL_SELECT_INPUT = IOMUXC_I2C1_SCL_SELECT_INPUT_DAISY(1); + IOMUXC_I2C1_SDA_SELECT_INPUT = IOMUXC_I2C1_SDA_SELECT_INPUT_DAISY(1); + + IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL = + IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SCL_HYS_MASK; + + IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA = + IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C1_SDA_HYS_MASK; +#endif /* CONFIG_I2C_1 */ + +#ifdef CONFIG_I2C_2 + IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL = + IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C2_SCL_SION_MASK; + IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA = + IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C2_SDA_SION_MASK; + + IOMUXC_I2C2_SCL_SELECT_INPUT = IOMUXC_I2C2_SCL_SELECT_INPUT_DAISY(1); + IOMUXC_I2C2_SDA_SELECT_INPUT = IOMUXC_I2C2_SDA_SELECT_INPUT_DAISY(1); + + IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL = + IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SCL_HYS_MASK; + + IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA = + IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C2_SDA_HYS_MASK; +#endif /* CONFIG_I2C_2 */ + +#ifdef CONFIG_I2C_3 + IOMUXC_SW_MUX_CTL_PAD_I2C3_SCL = + IOMUXC_SW_MUX_CTL_PAD_I2C3_SCL_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C3_SCL_SION_MASK; + IOMUXC_SW_MUX_CTL_PAD_I2C3_SDA = + IOMUXC_SW_MUX_CTL_PAD_I2C3_SDA_MUX_MODE(0) | + IOMUXC_SW_MUX_CTL_PAD_I2C3_SDA_SION_MASK; + + IOMUXC_I2C3_SCL_SELECT_INPUT = IOMUXC_I2C3_SCL_SELECT_INPUT_DAISY(2); + IOMUXC_I2C3_SDA_SELECT_INPUT = IOMUXC_I2C3_SDA_SELECT_INPUT_DAISY(2); + + IOMUXC_SW_PAD_CTL_PAD_I2C3_SCL = + IOMUXC_SW_PAD_CTL_PAD_I2C3_SCL_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SCL_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SCL_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SCL_HYS_MASK; + + IOMUXC_SW_PAD_CTL_PAD_I2C3_SDA = + IOMUXC_SW_PAD_CTL_PAD_I2C3_SDA_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SDA_PS(3) | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SDA_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_I2C3_SDA_HYS_MASK; +#endif /* CONFIG_I2C_3 */ + +#ifdef CONFIG_I2C_4 + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD2 = + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD2_MUX_MODE(3) | + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD2_SION_MASK; + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD3 = + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD3_MUX_MODE(3) | + IOMUXC_SW_MUX_CTL_PAD_ENET1_RGMII_TD3_SION_MASK; + + IOMUXC_I2C4_SCL_SELECT_INPUT = IOMUXC_I2C4_SCL_SELECT_INPUT_DAISY(4); + IOMUXC_I2C4_SDA_SELECT_INPUT = IOMUXC_I2C4_SDA_SELECT_INPUT_DAISY(4); + + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD2 = + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD2_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD2_PS(1) | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD2_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD2_HYS_MASK; + + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD3 = + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD3_PE_MASK | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD3_PS(1) | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD3_DSE(0) | + IOMUXC_SW_PAD_CTL_PAD_ENET1_RGMII_TD3_HYS_MASK; +#endif /* CONFIG_I2C_4 */ + return 0; } diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 59fbb73bc7fde..1d21b3ed1f6e5 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_CC32XX i2c_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_DW i2c_dw.c) zephyr_library_sources_ifdef(CONFIG_I2C_ESP32 i2c_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2C_GPIO i2c_gpio.c) +zephyr_library_sources_ifdef(CONFIG_I2C_IMX i2c_imx.c) zephyr_library_sources_ifdef(CONFIG_I2C_MCUX i2c_mcux.c) zephyr_library_sources_ifdef(CONFIG_NRFX_TWI i2c_nrfx_twi.c) zephyr_library_sources_ifdef(CONFIG_NRFX_TWIM i2c_nrfx_twim.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b9e9467dc4c6f..3a1922d1a9ead 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -202,6 +202,13 @@ config I2C_MCUX help Enable the mcux I2C driver. +config I2C_IMX + bool "i.MX I2C driver" + depends on HAS_IMX_I2C + select HAS_DTS_I2C + help + Enable the i.MX I2C driver. + config I2C_CC32XX bool "CC32XX I2C driver" depends on SOC_SERIES_CC32XX diff --git a/drivers/i2c/i2c_imx.c b/drivers/i2c/i2c_imx.c new file mode 100644 index 0000000000000..96cb646db35dc --- /dev/null +++ b/drivers/i2c/i2c_imx.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2018 Diego Sueiro, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "i2c-priv.h" + +#define DEV_CFG(dev) \ + ((const struct i2c_imx_config * const)(dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct i2c_imx_data * const)(dev)->driver_data) +#define DEV_BASE(dev) \ + ((I2C_Type *)(DEV_CFG(dev))->base) + +struct i2c_imx_config { + I2C_Type *base; + void (*irq_config_func)(struct device *dev); + u32_t bitrate; +}; + +struct i2c_master_transfer { + const u8_t *txBuff; + volatile u8_t *rxBuff; + volatile u32_t cmdSize; + volatile u32_t txSize; + volatile u32_t rxSize; + volatile bool isBusy; + volatile u32_t currentDir; + volatile u32_t currentMode; + volatile bool ack; +}; + +struct i2c_imx_data { + struct i2c_master_transfer transfer; + struct k_sem device_sync_sem; +}; + +static bool i2c_imx_write(struct device *dev, u8_t *txBuffer, u8_t txSize) +{ + I2C_Type *base = DEV_BASE(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + struct i2c_master_transfer *transfer = &data->transfer; + + transfer->isBusy = true; + + /* Clear I2C interrupt flag to avoid spurious interrupt */ + I2C_ClearStatusFlag(base, i2cStatusInterrupt); + + /* Set I2C work under Tx mode */ + I2C_SetDirMode(base, i2cDirectionTransmit); + transfer->currentDir = i2cDirectionTransmit; + + transfer->txBuff = txBuffer; + transfer->txSize = txSize; + + I2C_WriteByte(base, *transfer->txBuff); + transfer->txBuff++; + transfer->txSize--; + + /* Enable I2C interrupt, subsequent data transfer will be handled + * in ISR. + */ + I2C_SetIntCmd(base, true); + + /* Wait for the transfer to complete */ + k_sem_take(&data->device_sync_sem, K_FOREVER); + + return transfer->ack; +} + +static void i2c_imx_read(struct device *dev, u8_t *rxBuffer, u8_t rxSize) +{ + I2C_Type *base = DEV_BASE(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + struct i2c_master_transfer *transfer = &data->transfer; + + transfer->isBusy = true; + + /* Clear I2C interrupt flag to avoid spurious interrupt */ + I2C_ClearStatusFlag(base, i2cStatusInterrupt); + + /* Change to receive state. */ + I2C_SetDirMode(base, i2cDirectionReceive); + transfer->currentDir = i2cDirectionReceive; + + transfer->rxBuff = rxBuffer; + transfer->rxSize = rxSize; + + if (transfer->rxSize == 1) { + /* Send Nack */ + I2C_SetAckBit(base, false); + } else { + /* Send Ack */ + I2C_SetAckBit(base, true); + } + + /* dummy read to clock in 1st byte */ + I2C_ReadByte(base); + + /* Enable I2C interrupt, subsequent data transfer will be handled + * in ISR. + */ + I2C_SetIntCmd(base, true); + + /* Wait for the transfer to complete */ + k_sem_take(&data->device_sync_sem, K_FOREVER); + +} + +static int i2c_imx_configure(struct device *dev, u32_t dev_config_raw) +{ + I2C_Type *base = DEV_BASE(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + struct i2c_master_transfer *transfer = &data->transfer; + u32_t baudrate; + + if (!(I2C_MODE_MASTER & dev_config_raw)) { + return -EINVAL; + } + + if (I2C_ADDR_10_BITS & dev_config_raw) { + return -EINVAL; + } + + /* Initialize I2C state structure content. */ + transfer->txBuff = 0; + transfer->rxBuff = 0; + transfer->cmdSize = 0; + transfer->txSize = 0; + transfer->rxSize = 0; + transfer->isBusy = false; + transfer->currentDir = i2cDirectionReceive; + transfer->currentMode = i2cModeSlave; + + switch (I2C_SPEED_GET(dev_config_raw)) { + case I2C_SPEED_STANDARD: + baudrate = KHZ(100); + break; + case I2C_SPEED_FAST: + baudrate = MHZ(1); + break; + default: + return -EINVAL; + } + + /* Setup I2C init structure. */ + i2c_init_config_t i2cInitConfig = { + .baudRate = baudrate, + .slaveAddress = 0x00 + }; + + i2cInitConfig.clockRate = get_i2c_clock_freq(base); + + I2C_Init(base, &i2cInitConfig); + + I2C_Enable(base); + + return 0; +} + +static int i2c_imx_send_addr(struct device *dev, u16_t addr, u8_t flags) +{ + u8_t byte0 = addr << 1; + + byte0 |= (flags & I2C_MSG_RW_MASK) == I2C_MSG_READ; + return i2c_imx_write(dev, &byte0, 1); +} + +static int i2c_imx_transfer(struct device *dev, struct i2c_msg *msgs, + u8_t num_msgs, u16_t addr) +{ + I2C_Type *base = DEV_BASE(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + struct i2c_master_transfer *transfer = &data->transfer; + u8_t *buf, *buf_end; + int result = -EIO; + + if (!num_msgs) { + return 0; + } + + /* Make sure we're in a good state so slave recognises the Start */ + I2C_SetWorkMode(base, i2cModeSlave); + transfer->currentMode = i2cModeSlave; + /* Switch back to Rx direction. */ + I2C_SetDirMode(base, i2cDirectionReceive); + transfer->currentDir = i2cDirectionReceive; + /* Start condition */ + I2C_SetDirMode(base, i2cDirectionTransmit); + transfer->currentDir = i2cDirectionTransmit; + I2C_SetWorkMode(base, i2cModeMaster); + transfer->currentMode = i2cModeMaster; + + /* Send address after any Start condition */ + if (!i2c_imx_send_addr(dev, addr, msgs->flags)) { + goto finish; /* No ACK received */ + } + + do { + if (msgs->flags & I2C_MSG_RESTART) { + I2C_SendRepeatStart(base); + if (!i2c_imx_send_addr(dev, addr, msgs->flags)) { + goto finish; /* No ACK received */ + } + } + + /* Transfer data */ + buf = msgs->buf; + buf_end = buf + msgs->len; + if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) { + i2c_imx_read(dev, msgs->buf, msgs->len); + } else { + if (!i2c_imx_write(dev, msgs->buf, msgs->len)) { + goto finish; /* No ACK received */ + } + } + + if (msgs->flags & I2C_MSG_STOP) { + I2C_SetWorkMode(base, i2cModeSlave); + transfer->currentMode = i2cModeSlave; + I2C_SetDirMode(base, i2cDirectionReceive); + transfer->currentDir = i2cDirectionReceive; + } + + /* Next message */ + msgs++; + num_msgs--; + } while (num_msgs); + + /* Complete without error */ + result = 0; + return result; + +finish: + I2C_SetWorkMode(base, i2cModeSlave); + transfer->currentMode = i2cModeSlave; + I2C_SetDirMode(base, i2cDirectionReceive); + transfer->currentDir = i2cDirectionReceive; + + return result; +} + + +static void i2c_imx_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + I2C_Type *base = DEV_BASE(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + struct i2c_master_transfer *transfer = &data->transfer; + + /* Clear interrupt flag. */ + I2C_ClearStatusFlag(base, i2cStatusInterrupt); + + + /* Exit the ISR if no transfer is happening for this instance. */ + if (!transfer->isBusy) + return; + + if (i2cModeMaster == transfer->currentMode) { + if (i2cDirectionTransmit == transfer->currentDir) { + /* Normal write operation. */ + transfer->ack = + !(I2C_GetStatusFlag(base, i2cStatusReceivedAck)); + + if (transfer->txSize == 0) { + /* Close I2C interrupt. */ + I2C_SetIntCmd(base, false); + /* Release I2C Bus. */ + transfer->isBusy = false; + k_sem_give(&data->device_sync_sem); + } else { + I2C_WriteByte(base, *transfer->txBuff); + transfer->txBuff++; + transfer->txSize--; + } + } else { + /* Normal read operation. */ + if (transfer->rxSize == 2) { + /* Send Nack */ + I2C_SetAckBit(base, false); + } else { + /* Send Ack */ + I2C_SetAckBit(base, true); + } + + if (transfer->rxSize == 1) { + /* Switch back to Tx direction to avoid + * additional I2C bus read. + */ + I2C_SetDirMode(base, i2cDirectionTransmit); + transfer->currentDir = i2cDirectionTransmit; + } + + *transfer->rxBuff = I2C_ReadByte(base); + transfer->rxBuff++; + transfer->rxSize--; + + /* receive finished. */ + if (transfer->rxSize == 0) { + /* Close I2C interrupt. */ + I2C_SetIntCmd(base, false); + /* Release I2C Bus. */ + transfer->isBusy = false; + k_sem_give(&data->device_sync_sem); + } + } + } +} + +static int i2c_imx_init(struct device *dev) +{ + const struct i2c_imx_config *config = DEV_CFG(dev); + struct i2c_imx_data *data = DEV_DATA(dev); + u32_t bitrate_cfg; + int error; + + k_sem_init(&data->device_sync_sem, 0, UINT_MAX); + + bitrate_cfg = _i2c_map_dt_bitrate(config->bitrate); + + error = i2c_imx_configure(dev, I2C_MODE_MASTER | bitrate_cfg); + if (error) { + return error; + } + + config->irq_config_func(dev); + + return 0; +} + +static const struct i2c_driver_api i2c_imx_driver_api = { + .configure = i2c_imx_configure, + .transfer = i2c_imx_transfer, +}; + +#ifdef CONFIG_I2C_1 +static void i2c_imx_config_func_1(struct device *dev); + +static const struct i2c_imx_config i2c_imx_config_1 = { + .base = (I2C_Type *)I2C_1_BASE_ADDRESS, + .irq_config_func = i2c_imx_config_func_1, + .bitrate = I2C_1_CLOCK_FREQUENCY, +}; + +static struct i2c_imx_data i2c_imx_data_1; + +DEVICE_AND_API_INIT(i2c_imx_1, I2C_1_LABEL, &i2c_imx_init, + &i2c_imx_data_1, &i2c_imx_config_1, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &i2c_imx_driver_api); + +static void i2c_imx_config_func_1(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(I2C_1_IRQ, I2C_1_IRQ_PRIORITY, + i2c_imx_isr, DEVICE_GET(i2c_imx_1), 0); + + irq_enable(I2C_1_IRQ); +} +#endif /* CONFIG_I2C_1 */ + +#ifdef CONFIG_I2C_2 +static void i2c_imx_config_func_2(struct device *dev); + +static const struct i2c_imx_config i2c_imx_config_2 = { + .base = (I2C_Type *)I2C_2_BASE_ADDRESS, + .irq_config_func = i2c_imx_config_func_2, + .bitrate = I2C_2_CLOCK_FREQUENCY, +}; + +static struct i2c_imx_data i2c_imx_data_2; + +DEVICE_AND_API_INIT(i2c_imx_2, I2C_2_LABEL, &i2c_imx_init, + &i2c_imx_data_2, &i2c_imx_config_2, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &i2c_imx_driver_api); + +static void i2c_imx_config_func_2(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(I2C_2_IRQ, I2C_2_IRQ_PRIORITY, + i2c_imx_isr, DEVICE_GET(i2c_imx_2), 0); + + irq_enable(I2C_2_IRQ); +} +#endif /* CONFIG_I2C_2 */ + +#ifdef CONFIG_I2C_3 +static void i2c_imx_config_func_3(struct device *dev); + +static const struct i2c_imx_config i2c_imx_config_3 = { + .base = (I2C_Type *)I2C_3_BASE_ADDRESS, + .irq_config_func = i2c_imx_config_func_3, + .bitrate = I2C_3_CLOCK_FREQUENCY, +}; + +static struct i2c_imx_data i2c_imx_data_3; + +DEVICE_AND_API_INIT(i2c_imx_3, I2C_3_LABEL, &i2c_imx_init, + &i2c_imx_data_3, &i2c_imx_config_3, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &i2c_imx_driver_api); + +static void i2c_imx_config_func_3(struct device *dev) +{ + ARG_UNUSED(dev); + + IRQ_CONNECT(I2C_3_IRQ, I2C_3_IRQ_PRIORITY, + i2c_imx_isr, DEVICE_GET(i2c_imx_3), 0); + + irq_enable(I2C_3_IRQ); +} +#endif /* CONFIG_I2C_3 */ + +#ifdef CONFIG_I2C_4 +static void i2c_imx_config_func_4(struct device *dev); + +static const struct i2c_imx_config i2c_imx_config_4 = { + .base = (I2C_Type *)I2C_4_BASE_ADDRESS, + .irq_config_func = i2c_imx_config_func_4, + .bitrate = I2C_4_CLOCK_FREQUENCY, +}; + +static struct i2c_imx_data i2c_imx_data_4; + +DEVICE_AND_API_INIT(i2c_imx_4, I2C_4_LABEL, &i2c_imx_init, + &i2c_imx_data_4, &i2c_imx_config_4, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &i2c_imx_driver_api); + +static void i2c_imx_config_func_4(struct device *dev) +{ + ARG_UNUSED(dev); + IRQ_CONNECT(I2C_4_IRQ, I2C_4_IRQ_PRIORITY, + i2c_imx_isr, DEVICE_GET(i2c_imx_4), 0); + + irq_enable(I2C_4_IRQ); +} +#endif /* CONFIG_I2C_4 */ diff --git a/dts/arm/nxp/nxp_imx7d_m4.dtsi b/dts/arm/nxp/nxp_imx7d_m4.dtsi index 0dc200f1ac29e..2b677d50ad305 100644 --- a/dts/arm/nxp/nxp_imx7d_m4.dtsi +++ b/dts/arm/nxp/nxp_imx7d_m4.dtsi @@ -6,6 +6,7 @@ #include #include +#include / { cpus { @@ -201,6 +202,50 @@ label = "UART_7"; status = "disabled"; }; + + i2c1: i2c@30A20000 { + compatible = "fsl,imx7d-i2c"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30A20000 0x10000>; + interrupts = <35 0>; + label = "I2C_1"; + status = "disabled"; + }; + + i2c2: i2c@30A30000 { + compatible = "fsl,imx7d-i2c"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30A30000 0x10000>; + interrupts = <36 0>; + label = "I2C_2"; + status = "disabled"; + }; + + i2c3: i2c@30A40000 { + compatible = "fsl,imx7d-i2c"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30A40000 0x10000>; + interrupts = <37 0>; + label = "I2C_3"; + status = "disabled"; + }; + + i2c4: i2c@30A50000 { + compatible = "fsl,imx7d-i2c"; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30A50000 0x10000>; + interrupts = <38 0>; + label = "I2C_4"; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/i2c/fsl,imx7d-i2c.yaml b/dts/bindings/i2c/fsl,imx7d-i2c.yaml new file mode 100644 index 0000000000000..97f8865dba977 --- /dev/null +++ b/dts/bindings/i2c/fsl,imx7d-i2c.yaml @@ -0,0 +1,33 @@ +# +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: i.MX I2C Controller +version: 0.1 + +description: > + This is a representation of the i.MX I2C nodes + +inherits: + !include i2c.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "fsl,imx7d-i2c" + + reg: + type: int + description: mmio register space + generation: define + category: required + + interrupts: + type: compound + category: required + description: required interrupts + generation: define diff --git a/ext/hal/nxp/imx/Kconfig b/ext/hal/nxp/imx/Kconfig index ae2711c6251c2..58cf6e2826c0d 100644 --- a/ext/hal/nxp/imx/Kconfig +++ b/ext/hal/nxp/imx/Kconfig @@ -28,4 +28,9 @@ config HAS_IMX_GPIO help Set if the GPIO module is present in the SoC. +config HAS_IMX_I2C + bool + help + Set if the I2C module is present in the SoC. + endif # HAS_IMX_HAL diff --git a/ext/hal/nxp/imx/drivers/CMakeLists.txt b/ext/hal/nxp/imx/drivers/CMakeLists.txt index cb17a79ddae87..2a30b651bc6af 100644 --- a/ext/hal/nxp/imx/drivers/CMakeLists.txt +++ b/ext/hal/nxp/imx/drivers/CMakeLists.txt @@ -12,3 +12,4 @@ endif() zephyr_sources_ifdef(CONFIG_UART_IMX uart_imx.c) zephyr_sources_ifdef(CONFIG_GPIO_IMX gpio_imx.c) +zephyr_sources_ifdef(CONFIG_I2C_IMX i2c_imx.c)