From cd4fdf27c5ada07c012d6c04feb3fece00252f7b Mon Sep 17 00:00:00 2001 From: adriendesp Date: Tue, 23 Jul 2024 17:05:04 +0200 Subject: [PATCH] arch/xmc4 : i2c driver Added lower half i2c driver --- arch/arm/src/xmc4/CMakeLists.txt | 4 + arch/arm/src/xmc4/Make.defs | 4 + arch/arm/src/xmc4/xmc4_i2c.c | 1063 ++++++++++++++++++++++++++++++ arch/arm/src/xmc4/xmc4_i2c.h | 1 - 4 files changed, 1071 insertions(+), 1 deletion(-) create mode 100644 arch/arm/src/xmc4/xmc4_i2c.c diff --git a/arch/arm/src/xmc4/CMakeLists.txt b/arch/arm/src/xmc4/CMakeLists.txt index d46b1a2dbf..faa35c3ec2 100644 --- a/arch/arm/src/xmc4/CMakeLists.txt +++ b/arch/arm/src/xmc4/CMakeLists.txt @@ -51,6 +51,10 @@ if(CONFIG_XMC4_USCI_SPI) list(APPEND SRCS xmc4_spi.c) endif() +if(CONFIG_XMC4_USCI_I2C) + list(APPEND SRCS xmc4_i2c.c) +endif() + if(CONFIG_XMC4_PWM) list(APPEND SRCS xmc4_pwm.c) endif() diff --git a/arch/arm/src/xmc4/Make.defs b/arch/arm/src/xmc4/Make.defs index e7cb6d3b22..832d3c82e2 100644 --- a/arch/arm/src/xmc4/Make.defs +++ b/arch/arm/src/xmc4/Make.defs @@ -52,6 +52,10 @@ ifeq ($(CONFIG_XMC4_USCI_SPI),y) CHIP_CSRCS += xmc4_spi.c endif +ifeq ($(CONFIG_XMC4_USCI_I2C),y) +CHIP_CSRCS += xmc4_i2c.c +endif + ifeq ($(CONFIG_XMC4_ECAT),y) CHIP_CSRCS += xmc4_ecat.c endif diff --git a/arch/arm/src/xmc4/xmc4_i2c.c b/arch/arm/src/xmc4/xmc4_i2c.c new file mode 100644 index 0000000000..a7cd996afc --- /dev/null +++ b/arch/arm/src/xmc4/xmc4_i2c.c @@ -0,0 +1,1063 @@ +/***************************************************************************** + * arch/arm/src/xmc4/xmc4_i2c.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + *****************************************************************************/ + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "arm_internal.h" +#include "chip.h" + +#include "hardware/xmc4_pinmux.h" +#include "hardware/xmc4_memorymap.h" +#include "hardware/xmc4_usic.h" +#include "xmc4_i2c.h" +#include "xmc4_usic.h" +#include "xmc4_gpio.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_XMC4_USCI_I2C) + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +#define XMC_I2C_CLOCK_OVERSAMPLING_STANDARD (10U) +#define XMC_I2C_CLOCK_OVERSAMPLING_FAST (25U) +#define XMC_I2C_MAX_SPEED_STANDARD 100000UL +#define XMC_I2C_MAX_SPEED_FAST 400000 +#define I2C_DEFAULT_FREQUENCY 400000 + +#define I2C_INPUT_DX_SDA (0U) /* I2C uses DX0 input stage as SDA */ +#define I2C_INPUT_DX_SCL (1U) /* I2C uses DX1 input stage as SCL */ + +#define XMC_I2C_TDF_MASTER_SEND (0U) +#define XMC_I2C_TDF_SLAVE_SEND (1U << 8) +#define XMC_I2C_TDF_MASTER_RECEIVE_ACK (2U << 8) +#define XMC_I2C_TDF_MASTER_RECEIVE_NACK (3U << 8) +#define XMC_I2C_TDF_MASTER_START (4U << 8) +#define XMC_I2C_TDF_MASTER_RESTART (5U << 8) +#define XMC_I2C_TDF_MASTER_STOP (6U << 8) + +#define XMC_I2C_CMD_WRITE (0U) +#define XMC_I2C_CMD_READ (1U) + +#define I2C_WORDLENGTH (7U) /* 8 bits word length */ +#define I2C_TRM_MODE (3U) /* Shift and transfert config */ + +#define I2C_TDV_SET (1U) /* A transmission of data in TBUF \ + * can be started if TDV = 1 */ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +typedef enum xmc_usic_ch_input +{ + XMC_USIC_CH_INPUT_DX0, /* DX0 input */ + XMC_USIC_CH_INPUT_DX1, /* DX1 input */ + XMC_USIC_CH_INPUT_DX2, /* DX2 input */ + XMC_USIC_CH_INPUT_DX3, /* DX3 input */ + XMC_USIC_CH_INPUT_DX4, /* DX4 input */ + XMC_USIC_CH_INPUT_DX5 /* DX5 input */ +} xmc_usic_ch_input_t; + +typedef enum xmc_usic_ch_source +{ + XMC_USIC_CH_INPUT_DXNA, /* DXnA source signal */ + XMC_USIC_CH_INPUT_DXNB, /* DXnB source signal */ + XMC_USIC_CH_INPUT_DXNC, /* DXnC source signal */ + XMC_USIC_CH_INPUT_DXND, /* DXnD source signal */ + XMC_USIC_CH_INPUT_DXNE, /* DXnE source signal */ + XMC_USIC_CH_INPUT_DXNF, /* DXnF source signal */ + XMC_USIC_CH_INPUT_DXNG, /* DXnG source signal */ + XMC_USIC_CH_INPUT_DXN1 /* Signal always high */ +} xmc_usic_ch_source_t; + +/* This structure represents the state of one i2c channel */ + +struct xmc4_i2cdev_s +{ + const struct i2c_ops_s *ops; /* Generic I2C device operation */ + const uint32_t base; /* Base address of registers */ + const enum usic_channel_e channel; /* USIC channel */ + + uint32_t frequency; /* Current I2C frequency */ + + xmc_usic_ch_source_t dx0_sda; /* Source signal of SDA */ + xmc_usic_ch_source_t dx1_scl; /* Source signal of SCL */ + uint32_t sda_gpio; /* GPIO config of SDA */ + uint32_t scl_gpio; /* GPIO config of SCL */ + + mutex_t lock; /* Only one thread can access at a time */ + int refs; /* Reference count */ +}; + +/***************************************************************************** + * Private Function Prototypes + *****************************************************************************/ + +static inline void xmc4_i2c_putreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset, + uint32_t value); +static inline uint32_t xmc4_i2c_getreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset); +static inline void xmc4_i2c_modifyreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset, + uint32_t clearbits, + uint32_t setbits); + +static int i2c_set_baudrate(struct xmc4_i2cdev_s *priv); +static int i2c_set_input_source(struct xmc4_i2cdev_s *priv); +static void i2c_start_channel(struct xmc4_i2cdev_s *priv); +static void i2c_stop_channel(struct xmc4_i2cdev_s *priv); +static bool i2c_get_status_flag(struct xmc4_i2cdev_s *priv, + const uint32_t flag_mask); +static int i2c_clear_status_flag(struct xmc4_i2cdev_s *priv, + const uint32_t flag_mask); +static bool i2c_get_transmit_buffer_status(struct xmc4_i2cdev_s *priv); +static void i2c_fill_transmit_buffer(struct xmc4_i2cdev_s *priv, + const uint32_t data); +static uint8_t i2c_get_receive_buffer(struct xmc4_i2cdev_s *priv); + +static void i2c_start(struct xmc4_i2cdev_s *priv, + const uint16_t addr, + const bool read, + const bool restart); +static void i2c_stop(struct xmc4_i2cdev_s *priv); +static void i2c_master_transmit(struct xmc4_i2cdev_s *priv, + const uint8_t data); +static void i2c_master_ack(struct xmc4_i2cdev_s *priv); +static void i2c_master_nack(struct xmc4_i2cdev_s *priv); +static void i2c_wait_for_slave_ack(struct xmc4_i2cdev_s *priv); +static void i2c_wait_for_received_data_ready(struct xmc4_i2cdev_s *priv); + +static int i2c_initialize(struct xmc4_i2cdev_s *priv); +static int i2c_uninitialize(struct xmc4_i2cdev_s *priv); + +static int i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, + int count); +#ifdef CONFIG_I2C_RESET +static int i2c_reset(struct i2c_master_s *dev); +#endif +/***************************************************************************** + * Private Data + *****************************************************************************/ + +struct i2c_ops_s xmc4_i2c_ops = +{ + .transfer = i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = i2c_reset, +#endif +}; + +#ifdef CONFIG_XMC4_I2C0 +static struct xmc4_i2cdev_s g_i2c0 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC0_CH0_BASE, + .channel = USIC0_CHAN0, + .dx0_sda = BOARD_I2C0_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C0_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C0_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C0_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +#ifdef CONFIG_XMC4_I2C1 +static struct xmc4_i2cdev_s g_i2c1 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC0_CH1_BASE, + .channel = USIC0_CHAN1, + .dx0_sda = BOARD_I2C1_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C1_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C1_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C1_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +#ifdef CONFIG_XMC4_I2C2 +static struct xmc4_i2cdev_s g_i2c2 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC1_CH0_BASE, + .channel = USIC1_CHAN0, + .dx0_sda = BOARD_I2C2_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C2_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C2_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C2_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +#ifdef CONFIG_XMC4_I2C3 +static struct xmc4_i2cdev_s g_i2c3 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC1_CH1_BASE, + .channel = USIC1_CHAN1, + .dx0_sda = BOARD_I2C3_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C3_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C3_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C3_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +#ifdef CONFIG_XMC4_I2C4 +static struct xmc4_i2cdev_s g_i2c4 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC2_CH0_BASE, + .channel = USIC2_CHAN0, + .dx0_sda = BOARD_I2C4_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C4_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C4_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C4_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +#ifdef CONFIG_XMC4_I2C5 +static struct xmc4_i2cdev_s g_i2c5 = +{ + .ops = &xmc4_i2c_ops, + .base = XMC4_USIC2_CH1_BASE, + .channel = USIC2_CHAN1, + .dx0_sda = BOARD_I2C5_SDA_DX, /* See i2c_set_input_source */ + .dx1_scl = BOARD_I2C5_SCL_DX, /* See i2c_set_input_source */ + .sda_gpio = GPIO_I2C5_SDA, /* See i2c_set_input_source */ + .scl_gpio = GPIO_I2C5_SCL, /* See i2c_set_input_source */ + .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY, + .lock = NXMUTEX_INITIALIZER, + .refs = 0, +}; +#endif + +/***************************************************************************** + * Private Function + *****************************************************************************/ + +/* I2C register related functions */ + +static inline void xmc4_i2c_putreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +static inline uint32_t xmc4_i2c_getreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +static inline void xmc4_i2c_modifyreg32(struct xmc4_i2cdev_s *priv, + uint32_t offset, + uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->base + offset, clearbits, setbits); +} + +/***************************************************************************** + * Name: i2c_initialize + * + * Description: + * Initialize USIC channel for I2C operations. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_initialize(struct xmc4_i2cdev_s *priv) +{ + int ret; + + ret = xmc4_enable_usic_channel(priv->channel); + + /* Data format configuration */ + + uint32_t sctr = ((uint32_t)I2C_TRM_MODE << (uint32_t)USIC_SCTR_TRM_SHIFT) | + ((uint32_t)I2C_WORDLENGTH << (uint32_t)USIC_SCTR_WLE_SHIFT) | + (uint32_t)USIC_SCTR_FLE_MASK | /* unlimited data flow */ + (uint32_t)USIC_SCTR_SDIR | /* MSB shifted first */ + (uint32_t)USIC_SCTR_PDL; /* Passive Data Level */ + xmc4_i2c_putreg32(priv, XMC4_USIC_SCTR_OFFSET, sctr); + + /* Set baudrate */ + + ret |= i2c_set_baudrate(priv); + + /* Enable transfer buffer */ + + uint32_t tcsr = ((uint32_t)I2C_TDV_SET << (uint32_t)USIC_TCSR_TDEN_SHIFT) | + (uint32_t)USIC_TCSR_TDSSM; + xmc4_i2c_putreg32(priv, XMC4_USIC_TCSR_OFFSET, tcsr); + + /* Clear status flags */ + + ret |= i2c_clear_status_flag(priv, 0x1ffff); + + /* Disable parity generation */ + + xmc4_i2c_putreg32(priv, XMC4_USIC_CCR_OFFSET, 0); + + /* Set DX0m and DX1n as input sources for SDA & SCL */ + + ret |= i2c_set_input_source(priv); + + /* Set USIC channel in I2C mode */ + + i2c_start_channel(priv); + + /* Configure signal outputs pin */ + + xmc4_gpio_config((gpioconfig_t)priv->sda_gpio); /* GPIO_I2C_SDA is DOUT0 */ + xmc4_gpio_config((gpioconfig_t)priv->scl_gpio); /* GPIO_I2C_SCL is SCLKOUT */ + + return ret; +} + +/***************************************************************************** + * Name: i2c_uninitialize + * + * Description: + * Reset USIC channel. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_uninitialize(struct xmc4_i2cdev_s *priv) +{ + xmc4_i2c_putreg32(priv, XMC4_USIC_PSCR_OFFSET, 0); + xmc4_i2c_putreg32(priv, XMC4_USIC_PCR_OFFSET, 0); + xmc4_i2c_putreg32(priv, XMC4_USIC_CCR_OFFSET, 0); + + return xmc4_disable_usic_channel(priv->channel); +} + +/***************************************************************************** + * Name: i2c_set_baudrate + * + * Description: + * Set the baudrate of the USIC channel. Baudrate should be in [100-400k]Hz. + * Baudrate can't be changed while the USIC is running. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_set_baudrate(struct xmc4_i2cdev_s *priv) +{ + int ret; + + if (priv->frequency <= (uint32_t)XMC_I2C_MAX_SPEED_STANDARD) + { + xmc4_i2c_modifyreg32(priv, XMC4_USIC_PCR_OFFSET, USIC_PCR_IICMODE_STIM, + ~USIC_PCR_IICMODE_STIM); + + ret = xmc4_usic_baudrate(priv->channel, priv->frequency, + XMC_I2C_CLOCK_OVERSAMPLING_STANDARD); + } + else if (priv->frequency <= (uint32_t)XMC_I2C_MAX_SPEED_FAST) + { + xmc4_i2c_modifyreg32(priv, XMC4_USIC_PCR_OFFSET, USIC_PCR_IICMODE_STIM, + USIC_PCR_IICMODE_STIM); + + ret = xmc4_usic_baudrate(priv->channel, priv->frequency, + XMC_I2C_CLOCK_OVERSAMPLING_FAST); + } + else + { + ret = -EINVAL; + i2cerr("Selected baudrate isn't supported : [100-400k]Hz\n"); + } + + return ret; +} + +/***************************************************************************** + * Name: i2c_set_input_source + * + * Description: + * Sets the input source for I2C. Defines the input stage for the + * corresponding input line. BOARD_I2Cx_SDA_DX, BOARD_I2Cx_SCL_DX, + * GPIO_I2Cx_SDA and GPIO_I2Cx_SCL must be defined in board.h + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_set_input_source(struct xmc4_i2cdev_s *priv) +{ + /* Configure I2C pins and input sources. + * + * NOTE that the board must provide the definitions in the board.h header + * file of the form like: GPIO_I2Cx_SDA and GPIO_I2Cx_SCL where x is + * the I2C port number (U0C0 = 0, U0C1 = 1, U1C0 = 3 ... U2C1 = 5) + * + * In additional, the board.h must provide the definition of + * BOARD_I2Cx_SDA_DX and BOARD_I2Cx_SCL_DX which indicates which input + * source signal is selected, (0=DX_A, 1=DX_B, ... 6=DX_G). + * Use enum uart_dx_e. + */ + + if ((priv->dx0_sda > XMC_USIC_CH_INPUT_DXN1) || + (priv->dx1_scl > XMC_USIC_CH_INPUT_DXN1)) + { + return -EINVAL; + } + + /* Set DX0 config */ + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX0CR_OFFSET, + USIC_DXCR_INSW, USIC_DXCR_DSEN); + + /* Set DX0 source [G:A] */ + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX0CR_OFFSET, + USIC_DXCR_DSEL_MASK, USIC_DXCR_DSEL_DX(priv->dx0_sda)); + + /* Set DX1 config */ + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX1CR_OFFSET, + USIC_DXCR_INSW, USIC_DXCR_DSEN); + + /* Set DX1 source [G:A] */ + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX1CR_OFFSET, + USIC_DXCR_DSEL_MASK, USIC_DXCR_DSEL_DX(priv->dx1_scl)); + + return OK; +} + +/***************************************************************************** + * Name: i2c_start_channel + * + * Description: + * Leave USIC channel from idle mode and set I2C mode. + * + *****************************************************************************/ + +static void i2c_start_channel(struct xmc4_i2cdev_s *priv) +{ + /* Sets the USIC input operation mode to I2C mode using CCR register. */ + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_CCR_OFFSET, + USIC_CCR_MODE_MASK, USIC_CCR_MODE_I2C); +} + +/***************************************************************************** + * Name: i2c_stop_channel + * + * Description: + * Wait for current transmit to end and set the USIC channel in idle mode. + * + *****************************************************************************/ + +static void i2c_stop_channel(struct xmc4_i2cdev_s *priv) +{ + /* Sets the USIC input operation mode to idle mode using CCR register. */ + + while (i2c_get_transmit_buffer_status(priv) == 1) + { + /* wait for transmit to end */ + } + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_CCR_OFFSET, USIC_CCR_MODE_MASK, 0); +} + +/***************************************************************************** + * Name: i2c_get_status_flag + * + * Description: + * Return the flags from PSR register. + * + * Returned Value: + * FALSE if selected flags are 0 + * else TRUE if one of the selected flags isn't 0. + * + *****************************************************************************/ + +static bool i2c_get_status_flag(struct xmc4_i2cdev_s *priv, + const uint32_t flag_mask) +{ + uint32_t psr = xmc4_i2c_getreg32(priv, XMC4_USIC_PSR_OFFSET); + + return ((psr & flag_mask) != 0); +} + +/***************************************************************************** + * Name: i2c_clear_status_flag + * + * Description: + * Clear the flags from PSR register by writting in PSCR register. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_clear_status_flag(struct xmc4_i2cdev_s *priv, + const uint32_t flag_mask) +{ + if (flag_mask > 0x1ffff) + { + return -EINVAL; + } + + xmc4_i2c_modifyreg32(priv, XMC4_USIC_PSCR_OFFSET, 0, flag_mask); + + return OK; +} + +/***************************************************************************** + * Name: i2c_get_transmit_buffer_status + * + * Description: + * Get the status of the transmit buffer. + * + * Returned Value: + * ZERO if idle, else ONE if busy. + * + *****************************************************************************/ + +static bool i2c_get_transmit_buffer_status(struct xmc4_i2cdev_s *priv) +{ + uint32_t tbuf_stat = xmc4_i2c_getreg32(priv, XMC4_USIC_TCSR_OFFSET); + + return (tbuf_stat & (uint32_t)USIC_TCSR_TDV); +} + +/***************************************************************************** + * Name: i2c_fill_transmit_buffer + * + * Description: + * Wait for transmit buffer to be ready and fill the transmit buffer + * with given word data. + * TODO : enable FIFO mechanism so you don't have to wait. + * + *****************************************************************************/ + +static void i2c_fill_transmit_buffer(struct xmc4_i2cdev_s *priv, + const uint32_t data) +{ + /* Check FIFO size */ + + uint32_t tbctr = xmc4_i2c_getreg32(priv, XMC4_USIC_TBCTR_OFFSET); + + if ((tbctr & USIC_TBCTR_SIZE_MASK) == 0) + { + /* FIFO mechanism is disabled */ + + while (i2c_get_transmit_buffer_status(priv) == 1) + { + /* check TDV, wait until TBUF is ready */ + } + + /* Clear Status Flag */ + + i2c_clear_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_TBIF); + + /* Put data in TBUF[0] */ + + xmc4_i2c_putreg32(priv, XMC4_USIC_TBUF_OFFSET, data); + } + else + { + /* Put data in IN[0] */ + + xmc4_i2c_putreg32(priv, XMC4_USIC_IN_OFFSET, data); + } +} + +/***************************************************************************** + * Name: i2c_get_receive_buffer + * + * Description: + * Get the data word from receive buffer. + * + * Returned Value: + * Recived data word in uint8_t. + * + *****************************************************************************/ + +static uint8_t i2c_get_receive_buffer(struct xmc4_i2cdev_s *priv) +{ + uint8_t retval; + + /* Check FIFO size */ + + uint32_t rbctr = xmc4_i2c_getreg32(priv, XMC4_USIC_RBCTR_OFFSET); + if ((rbctr & USIC_RBCTR_SIZE_SHIFT) == 0U) + { + retval = (uint8_t)xmc4_i2c_getreg32(priv, XMC4_USIC_RBUF_OFFSET); + } + else + { + retval = (uint8_t)xmc4_i2c_getreg32(priv, XMC4_USIC_OUTR_OFFSET); + } + + return retval; +} + +/***************************************************************************** + * Name: i2c_start + * + * Description: + * Start and send an I2C frame with the given slave adress. + * Send the following on bus : [START/RESTART A6 A5 A4 A3 A2 A1 A0 R/W] + * + *****************************************************************************/ + +static void i2c_start(struct xmc4_i2cdev_s *priv, + const uint16_t addr, + const bool read, + const bool restart) +{ + uint32_t sent_data = (addr << 1); + + /* Add start transition */ + + if (restart) + { + sent_data |= XMC_I2C_TDF_MASTER_RESTART; + } + else + { + sent_data |= XMC_I2C_TDF_MASTER_START; + } + + if (read) + { + sent_data |= 0x01; + } + + i2c_fill_transmit_buffer(priv, sent_data); +} + +/***************************************************************************** + * Name: i2c_stop + * + * Description: + * Stop an I2C frame. + * Send the following on bus : [STOP] + * + *****************************************************************************/ + +static void i2c_stop(struct xmc4_i2cdev_s *priv) +{ + uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_STOP; + i2c_fill_transmit_buffer(priv, buff_data); +} + +/***************************************************************************** + * Name: i2c_master_transmit + * + * Description: + * Transmit data on bus. + * Send the following on bus : [D7 D6 D5 D4 D3 D2 D1 D0] + * + *****************************************************************************/ + +static void i2c_master_transmit(struct xmc4_i2cdev_s *priv, + const uint8_t data) +{ + uint32_t buff_data = (uint32_t)(data | (uint32_t)XMC_I2C_TDF_MASTER_SEND); + i2c_fill_transmit_buffer(priv, buff_data); +} + +/***************************************************************************** + * Name: i2c_master_ack + * + * Description: + * Answer with a master acknowledge. + * Send the following on bus : [ACK] + * + *****************************************************************************/ + +static void i2c_master_ack(struct xmc4_i2cdev_s *priv) +{ + uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_RECEIVE_ACK; + i2c_fill_transmit_buffer(priv, buff_data); +} + +/***************************************************************************** + * Name: i2c_master_nack + * + * Description: + * Answer with a master not-acknowledge. + * Send the following on bus : [NACK] + * + *****************************************************************************/ + +static void i2c_master_nack(struct xmc4_i2cdev_s *priv) +{ + uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_RECEIVE_NACK; + i2c_fill_transmit_buffer(priv, buff_data); +} + +/***************************************************************************** + * Name: i2c_wait_for_slave_ack + * + * Description: + * Wait for the slave on bus to acknowledge. + * + *****************************************************************************/ + +static void i2c_wait_for_slave_ack(struct xmc4_i2cdev_s *priv) +{ + while (i2c_get_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_ACK) == false) + { + /* Wait for ACK */ + } + + i2c_clear_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_ACK); +} + +/***************************************************************************** + * Name: i2c_wait_for_received_data_ready + * + * Description: + * Wait for the received data buffer to be ready to be read. + * + *****************************************************************************/ + +static void i2c_wait_for_received_data_ready(struct xmc4_i2cdev_s *priv) +{ + while (i2c_get_status_flag(priv, (uint32_t)(USIC_PSR_IICMODE_RIF | + USIC_PSR_IICMODE_AIF)) == false) + { + /* Wait for RIF */ + } + + i2c_clear_status_flag(priv, (uint32_t)(USIC_PSR_IICMODE_RIF | + USIC_PSR_IICMODE_AIF)); +} + +/***************************************************************************** + * Name: i2c_transfer + * + * Description: + * Generic I2C transfer. + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + *****************************************************************************/ + +static int i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, + int count) +{ + struct xmc4_i2cdev_s *priv = (struct xmc4_i2cdev_s *)dev; + + int ret = OK; + + bool previous_msg_no_stop = false; + + /* Get exclusive access to the I2C bus */ + + nxmutex_lock(&priv->lock); + + for (int i = 0; i < count; i++) + { + /* Check if frequency must be changed */ + + if (msgs[i].frequency != priv->frequency) + { + priv->frequency = msgs[i].frequency; + + if (!previous_msg_no_stop) + { + i2c_stop_channel(priv); + + i2c_set_baudrate(priv); + + i2c_start_channel(priv); + } + else + { + i2cerr("Can't update frequency between Start & Stop symbols\n"); + nxmutex_unlock(&priv->lock); + return -EINVAL; + } + } + + /* R/W bit */ + + bool read = ((msgs[i].flags & I2C_M_READ) != 0); + + /* Enter critical section to avoid interrupts during i2c transfert */ + + irqstate_t state = enter_critical_section(); + + /* Check if you should start or restart a new i2c frame */ + + if ((msgs[i].flags & I2C_M_NOSTART) == 0) + { + if (previous_msg_no_stop) + { + /* Repeated start */ + + i2c_start(priv, msgs[i].addr, read, true); + i2c_wait_for_slave_ack(priv); + } + else + { + /* Normal start */ + + i2c_start(priv, msgs[i].addr, read, false); + i2c_wait_for_slave_ack(priv); + } + } + + /* Parse buffer data */ + + for (int j = 0; j < (msgs[i].length); j++) + { + if (read) + { + /* Answer ack (or nack if last buffer) */ + + if (j == ((msgs[i].length) - 1)) + { + i2c_master_nack(priv); + } + else + { + i2c_master_ack(priv); + } + + i2c_wait_for_received_data_ready(priv); + + /* Save received data */ + + msgs[i].buffer[j] = i2c_get_receive_buffer(priv); + } + else + { + i2c_master_transmit(priv, msgs[i].buffer[j]); + i2c_wait_for_slave_ack(priv); + } + } + + /* Check if you should not stop the i2c frame + * msg has NO_STOP or next msg has NO_START flag + */ + + bool message_has_no_stop = ((msgs[i].flags & I2C_M_NOSTOP) != 0); + + bool next_message_has_no_start = false; + + if (i < count) + { + next_message_has_no_start = + ((msgs[i + 1].flags & I2C_M_NOSTART) != 0); + } + + bool should_stop = !(message_has_no_stop || next_message_has_no_start); + + /* TODO : Should you stop when last msg and overide flags ? */ + + if (should_stop) + { + i2c_stop(priv); + } + else + { + previous_msg_no_stop = true; + } + } + + leave_critical_section(state); + nxmutex_unlock(&priv->lock); + return ret; +} + +#ifdef CONFIG_I2C_RESET +int i2c_reset(struct i2c_master_s *dev) +{ +#error not implemented +} +#endif + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: xmc4_i2cbus_initialize + * + * Description: + * Initialise an I2C device + * + *****************************************************************************/ + +struct i2c_master_s *xmc4_i2cbus_initialize(int port) +{ + struct xmc4_i2cdev_s *priv = NULL; + + switch (port) + { +#ifdef CONFIG_XMC4_I2C0 + case 0: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c0; + break; + } +#endif + +#ifdef CONFIG_XMC4_I2C1 + case 1: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c1; + break; + } +#endif + +#ifdef CONFIG_XMC4_I2C2 + case 2: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c2; + break; + } +#endif + +#ifdef CONFIG_XMC4_I2C3 + case 3: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c3; + break; + } +#endif + +#ifdef CONFIG_XMC4_I2C4 + case 4: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c4; + break; + } +#endif + +#ifdef CONFIG_XMC4_I2C5 + case 5: + { + priv = (struct xmc4_i2cdev_s *)&g_i2c5; + break; + } +#endif + + default: + { + i2cerr("No such i2c module.\n"); + return NULL; + } + } + + nxmutex_lock(&priv->lock); + + if (priv->refs++ == 0) + { + int ret = i2c_initialize(priv); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return NULL; + } + } + + nxmutex_unlock(&priv->lock); + + return (struct i2c_master_s *)priv; +} + +/***************************************************************************** + * Name: xmc4_i2cbus_uninitialize + * + * Description: + * Uninitialise an I2C device + * + *****************************************************************************/ + +int xmc4_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct xmc4_i2cdev_s *priv = (struct xmc4_i2cdev_s *)dev; + + if (priv->refs == 0) + { + return ERROR; + } + + nxmutex_lock(&priv->lock); + + if (--priv->refs) + { + nxmutex_unlock(&priv->lock); + kmm_free(dev); + return OK; + } + + i2c_uninitialize(priv); + + nxmutex_unlock(&priv->lock); + + kmm_free(dev); + + return OK; +} + +#endif /* CONFIG_XMC4_USCI_I2C */ diff --git a/arch/arm/src/xmc4/xmc4_i2c.h b/arch/arm/src/xmc4/xmc4_i2c.h index f79d46514d..c910f03877 100644 --- a/arch/arm/src/xmc4/xmc4_i2c.h +++ b/arch/arm/src/xmc4/xmc4_i2c.h @@ -27,7 +27,6 @@ #include #include -#include "hardware/xmc4_i2c.h" /**************************************************************************** * Public Function Prototypes