blob: 608b7585777f833c74c93092e57cf1fb2811ff8d [file] [log] [blame]
/*****************************************************************************
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/mutex.h>
#include <nuttx/irq.h>
#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);
/* Enter critical section to avoid interrupts during i2c transfert */
irqstate_t state = enter_critical_section();
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");
leave_critical_section(state);
nxmutex_unlock(&priv->lock);
return -EINVAL;
}
}
/* R/W bit */
bool read = ((msgs[i].flags & I2C_M_READ) != 0);
/* 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 */