s32k1xx: FlexIO I2C master driver
diff --git a/arch/arm/src/s32k1xx/Kconfig b/arch/arm/src/s32k1xx/Kconfig
index 35f9c75..2bd964f 100644
--- a/arch/arm/src/s32k1xx/Kconfig
+++ b/arch/arm/src/s32k1xx/Kconfig
@@ -227,6 +227,10 @@
default n
depends on S32K1XX_HAVE_FLEXCAN2
+config S32K1XX_FLEXIO_I2C
+ bool "FlexIO I2C (emulated I2C)"
+ default n
+
config S32K1XX_FTM0
bool "FTM0"
default n
diff --git a/arch/arm/src/s32k1xx/Make.defs b/arch/arm/src/s32k1xx/Make.defs
index 83d6d92..c3f94ac 100644
--- a/arch/arm/src/s32k1xx/Make.defs
+++ b/arch/arm/src/s32k1xx/Make.defs
@@ -54,6 +54,10 @@
CHIP_CSRCS += s32k1xx_lpi2c_slave.c
endif
+ifeq ($(CONFIG_S32K1XX_FLEXIO_I2C),y)
+CHIP_CSRCS += s32k1xx_flexio_i2c.c
+endif
+
ifeq ($(CONFIG_S32K1XX_LPSPI),y)
CHIP_CSRCS += s32k1xx_lpspi.c
endif
diff --git a/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.c b/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.c
new file mode 100644
index 0000000..9574bca
--- /dev/null
+++ b/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.c
@@ -0,0 +1,757 @@
+/****************************************************************************
+ * arch/arm/src/s32k1xx/s32k1xx_flexio_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 <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include <arch/irq.h>
+
+#include "arm_internal.h"
+#include "s32k1xx_edma.h"
+#include "s32k1xx_pin.h"
+#include "hardware/s32k1xx_dmamux.h"
+#include "hardware/s32k1xx_pinmux.h"
+#include "s32k1xx_flexio_i2c.h"
+#include "s32k1xx_periphclocks.h"
+#include "s32k1xx_pin.h"
+
+#include <arch/board/board.h>
+
+/* At least one I2C peripheral must be enabled */
+#define CONFIG_S32K1XX_FLEXIO_I2C
+#ifdef CONFIG_S32K1XX_FLEXIO_I2C
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define I2C_MAX_FREQUENCY 400000
+#define FLEXIO_TX_IRQ (1 << priv->config->tx_inst)
+#define FLEXIO_RX_IRQ (1 << priv->config->rx_inst)
+#define FLEXIO_ADDR_READ(addr) ((addr << 1 ) | 1)
+#define FLEXIO_ADDR_WRITE(addr) (addr << 1)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+enum s32k1xx_intstate_e
+{
+ INTSTATE_IDLE = 0, /* No I2C activity */
+ INTSTATE_RECV_DATA,
+ INTSTATE_WRITE_DATA,
+};
+
+struct s32k1xx_flexio_i2c_config_s
+{
+ int tx_inst;
+ int rx_inst;
+ int timer;
+ int two_word_timer;
+ uint32_t flexio_sda_pin;
+ uint32_t flexio_scl_pin;
+ uint32_t sda_pin;
+ uint32_t scl_pin;
+};
+
+struct s32k1xx_flexio_i2cdev_s
+{
+ struct i2c_master_s dev; /* Generic I2C device */
+ unsigned int base; /* Base address of registers */
+ uint16_t irqid; /* IRQ for this device */
+ int8_t port; /* Port number */
+ uint32_t base_freq; /* branch frequency */
+
+ mutex_t lock; /* Only one thread can access at a time */
+ sem_t wait; /* Place to wait for transfer completion */
+ uint32_t frequency; /* Current I2C frequency */
+ int32_t tx_count;
+ int32_t reg_buff_offset;
+ int32_t reg_buff_mod;
+ uint32_t tx_done;
+
+ struct i2c_msg_s *msgs;
+ int total_bytes;
+
+ /* Port configuration */
+
+ const struct s32k1xx_flexio_i2c_config_s *config;
+
+ enum s32k1xx_intstate_e state;
+
+ int error; /* Error status of each transfers */
+ int refs; /* Reference count */
+};
+
+static const struct s32k1xx_flexio_i2c_config_s s32k1xx_flexio_i2c0_config =
+{
+ .tx_inst = 0,
+ .rx_inst = 1,
+ .timer = 1,
+ .two_word_timer = 0,
+ .flexio_sda_pin = FLEXIO_I2C0_SDA,
+ .flexio_scl_pin = FLEXIO_I2C0_SCL,
+ .sda_pin = PIN_FLEXIO_I2C0_SCL,
+ .scl_pin = PIN_FLEXIO_I2C0_SDA,
+};
+
+static struct s32k1xx_flexio_i2cdev_s g_i2c0dev =
+{
+ .port = 0,
+ .base = S32K1XX_FLEXIO_BASE,
+ .irqid = S32K1XX_IRQ_FLEXIO,
+ .lock = NXMUTEX_INITIALIZER,
+ .wait = SEM_INITIALIZER(0),
+ .refs = 0,
+ .config = &s32k1xx_flexio_i2c0_config,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void s32k1xx_flexio_i2c_init(struct s32k1xx_flexio_i2cdev_s *priv);
+static int s32k1xx_flexio_i2c_interrupt(int irq, void *context, void *arg);
+static void s32k1xx_flexio_i2c_set_freq_and_size(
+ struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t frequency, uint8_t num_bytes);
+static int s32k1xx_flexio_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs, int count);
+
+static inline uint32_t flexio_getreg32(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t offset)
+{
+ return getreg32(priv->base + offset);
+}
+
+static inline void flexio_putreg32(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t val, uint32_t offset)
+{
+ putreg32(val, priv->base + offset);
+}
+
+static inline void flexio_modifyreg32(struct s32k1xx_flexio_i2cdev_s *priv,
+ unsigned int offset,
+ uint32_t clearbits,
+ uint32_t setbits)
+{
+ modifyreg32(priv->base + offset, clearbits, setbits);
+}
+
+static inline void
+enable_shifter_status_interrupts(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t mask)
+{
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_SHIFTSIEN_OFFSET, 0, mask);
+}
+
+static inline void
+disable_shifter_status_interrupts(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t mask)
+{
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_SHIFTSIEN_OFFSET, mask, 0);
+}
+
+static inline uint32_t
+get_shifter_status_flags(struct s32k1xx_flexio_i2cdev_s *priv)
+{
+ return flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTSTAT_OFFSET);
+}
+
+static inline void
+clear_shifter_status_flags(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t mask)
+{
+ flexio_putreg32(priv, mask, S32K1XX_FLEXIO_SHIFTSTAT_OFFSET);
+}
+
+static inline void
+enable_timer_status_interrupts(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t mask)
+{
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_TIMIEN_OFFSET, 0, mask);
+}
+
+static inline void
+disable_timer_status_interrupts(struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t mask)
+{
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_TIMIEN_OFFSET, mask, 0);
+}
+
+static inline void
+s32k1xx_flexio_i2c_set_nack(struct s32k1xx_flexio_i2cdev_s *priv)
+{
+ flexio_putreg32(priv, FLEXIO_SHIFTCFG_INSRC_PIN |
+ FLEXIO_SHIFTCFG_SSTOP_ONE |
+ FLEXIO_SHIFTCFG_SSTART_ZERO,
+ S32K1XX_FLEXIO_SHIFTCFG0_OFFSET + priv->config->tx_inst * 0x4);
+}
+
+static inline void
+s32k1xx_flexio_i2c_set_ack(struct s32k1xx_flexio_i2cdev_s *priv)
+{
+ flexio_putreg32(priv, FLEXIO_SHIFTCFG_INSRC_PIN |
+ FLEXIO_SHIFTCFG_SSTOP_ZERO |
+ FLEXIO_SHIFTCFG_SSTART_ZERO,
+ S32K1XX_FLEXIO_SHIFTCFG0_OFFSET + priv->config->tx_inst * 0x4);
+}
+
+static inline void
+s32k1xx_flexio_i2c_clear_errors(struct s32k1xx_flexio_i2cdev_s *priv)
+{
+ flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTBUFBIS0_OFFSET +
+ priv->config->rx_inst * 0x4);
+ flexio_putreg32(priv, FLEXIO_TX_IRQ | FLEXIO_RX_IRQ,
+ S32K1XX_FLEXIO_SHIFTERR_OFFSET);
+}
+
+/****************************************************************************
+ * I2C device operations
+ ****************************************************************************/
+
+struct i2c_ops_s s32k1xx_flexio_i2c_ops =
+{
+ .transfer = s32k1xx_flexio_i2c_transfer,
+};
+
+/****************************************************************************
+ * Name: s32k1xx_flexio_i2c_set_freq_and_size
+ *
+ * Description:
+ * Set the frequency for the next transfer
+ *
+ ****************************************************************************/
+
+static void s32k1xx_flexio_i2c_set_freq_and_size(
+ struct s32k1xx_flexio_i2cdev_s *priv,
+ uint32_t frequency, uint8_t num_bytes)
+{
+ int divider = ((priv->base_freq / frequency) / 2) - 1;
+
+ if (divider > 0xff)
+ {
+ divider = 0xff;
+ }
+
+ flexio_putreg32(priv, divider |
+ ((((num_bytes + 1) * 9 + 1) * 2 - 1) << 8),
+ S32K1XX_FLEXIO_TIMCMP0_OFFSET +
+ priv->config->two_word_timer * 0x4);
+}
+
+/****************************************************************************
+ * Name: s32k1xx_flexio_i2c_interrupt
+ *
+ * Description:
+ * The I2C Interrupt Handler
+ *
+ ****************************************************************************/
+
+static int s32k1xx_flexio_i2c_interrupt(int irq, void *context, void *arg)
+{
+ struct s32k1xx_flexio_i2cdev_s *priv =
+ (struct s32k1xx_flexio_i2cdev_s *)arg;
+ struct i2c_msg_s *msg = priv->msgs;
+
+ if ((priv->state == INTSTATE_IDLE))
+ {
+ disable_shifter_status_interrupts(priv, FLEXIO_RX_IRQ);
+ disable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ);
+ }
+ else if ((priv->state == INTSTATE_RECV_DATA))
+ {
+ if (!priv->tx_done && get_shifter_status_flags(priv) & FLEXIO_TX_IRQ)
+ {
+ if (priv->tx_count == msg->length)
+ {
+ /* Send NACK and Stop bit */
+
+ s32k1xx_flexio_i2c_set_nack(priv);
+ flexio_putreg32(priv, 0x00,
+ S32K1XX_FLEXIO_SHIFTBUFBBS0_OFFSET +
+ priv->config->tx_inst * 0x4);
+ priv->tx_count++;
+ }
+ else if (priv->tx_count < msg->length)
+ {
+ flexio_putreg32(priv, 0xff,
+ S32K1XX_FLEXIO_SHIFTBUFBBS0_OFFSET +
+ priv->config->tx_inst * 0x4);
+ priv->tx_count++;
+ }
+ else
+ {
+ priv->tx_done = 1;
+ disable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ);
+ }
+ }
+
+ if (get_shifter_status_flags(priv) & FLEXIO_RX_IRQ)
+ {
+ if (priv->reg_buff_offset < 0)
+ {
+ flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTBUFBIS0_OFFSET +
+ priv->config->rx_inst * 0x4);
+
+ /* If RX shifter reports an error then stop bit (ACK)
+ * doesn't go low, thus we get a NACK
+ */
+
+ if (flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTERR_OFFSET) &
+ FLEXIO_RX_IRQ)
+ {
+ priv->error = 1;
+ flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTBUFBIS0_OFFSET +
+ priv->config->rx_inst * 0x4);
+ flexio_putreg32(priv, FLEXIO_RX_IRQ,
+ S32K1XX_FLEXIO_SHIFTERR_OFFSET);
+ disable_shifter_status_interrupts(priv, FLEXIO_RX_IRQ);
+ disable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ);
+ priv->state = INTSTATE_IDLE;
+ nxsem_post(&priv->wait);
+ }
+ else if (msg->length > 1)
+ {
+ s32k1xx_flexio_i2c_set_ack(priv);
+ }
+ }
+ else
+ {
+ msg->buffer[priv->reg_buff_offset] =
+ flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTBUFBIS0_OFFSET +
+ priv->config->rx_inst * 0x4) & 0xff;
+ }
+
+ priv->reg_buff_offset++;
+ if (priv->reg_buff_offset == msg->length)
+ {
+ disable_shifter_status_interrupts(priv, FLEXIO_RX_IRQ);
+ disable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ);
+ priv->state = INTSTATE_IDLE;
+ nxsem_post(&priv->wait);
+ }
+ }
+ }
+ else if ((priv->state == INTSTATE_WRITE_DATA))
+ {
+ if (!priv->tx_done && get_shifter_status_flags(priv) & FLEXIO_TX_IRQ)
+ {
+ flexio_putreg32(priv, msg->buffer[priv->reg_buff_mod++],
+ S32K1XX_FLEXIO_SHIFTBUFBBS0_OFFSET +
+ priv->config->tx_inst * 0x4);
+ priv->tx_count++;
+
+ if (priv->reg_buff_mod % msg->length == 0)
+ {
+ priv->reg_buff_mod = 0;
+ priv->msgs++;
+ }
+
+ if (priv->tx_count == priv->total_bytes)
+ {
+ priv->tx_done = 1;
+ disable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ);
+ }
+ }
+
+ if (get_shifter_status_flags(priv) & FLEXIO_RX_IRQ)
+ {
+ flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTBUFBIS0_OFFSET +
+ priv->config->rx_inst * 0x4);
+
+ /* If RX shifter reports an error then stop bit (ACK)
+ * doesn't go low, thus we get a NACK
+ */
+
+ if (priv->reg_buff_offset == 0 &&
+ (flexio_getreg32(priv, S32K1XX_FLEXIO_SHIFTERR_OFFSET) &
+ FLEXIO_RX_IRQ))
+ {
+ priv->error = 1;
+ }
+
+ priv->reg_buff_offset++;
+ if (priv->reg_buff_offset == priv->total_bytes - 1)
+ {
+ disable_shifter_status_interrupts(priv, FLEXIO_RX_IRQ);
+ priv->state = INTSTATE_IDLE;
+ nxsem_post(&priv->wait);
+ }
+ }
+ }
+
+ return OK;
+}
+
+static int set_expiretime(int expire_time, FAR struct timespec *set_time)
+{
+ struct timespec curr_time;
+
+ /* Get current time. */
+
+ if (clock_gettime(CLOCK_REALTIME, &curr_time) != OK)
+ {
+ return ERROR;
+ }
+
+ set_time->tv_sec = expire_time / 1000;
+ set_time->tv_nsec =
+ (expire_time - (set_time->tv_sec * 1000)) * 1000 * 1000;
+
+ set_time->tv_sec += curr_time.tv_sec;
+ set_time->tv_nsec += curr_time.tv_nsec;
+
+ /* Check more than 1 sec. */
+
+ if (set_time->tv_nsec >= (1000 * 1000 * 1000))
+ {
+ set_time->tv_sec += 1;
+ set_time->tv_nsec -= (1000 * 1000 * 1000);
+ }
+
+ return OK;
+}
+
+static void s32k1xx_flexio_i2c_init(struct s32k1xx_flexio_i2cdev_s *priv)
+{
+ /* Reset FlexIO peripheral */
+
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_CTRL_OFFSET, 0,
+ FLEXIO_CTRL_SWRST);
+ flexio_putreg32(priv, 0, S32K1XX_FLEXIO_CTRL_OFFSET);
+
+ /* Initialize FlexIO peripheral */
+
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_CTRL_OFFSET,
+ (FLEXIO_CTRL_DOZEN |
+ FLEXIO_CTRL_DBGE |
+ FLEXIO_CTRL_FASTACC |
+ FLEXIO_CTRL_FLEXEN),
+ (FLEXIO_CTRL_DBGE |
+ FLEXIO_CTRL_FLEXEN_DIS));
+
+ /* Start bit enabled (logic 0) and stop bit enabled (logic 1). */
+
+ s32k1xx_flexio_i2c_set_nack(priv);
+
+ /* Transmit mode, output to FXIO pin */
+
+ flexio_putreg32(priv, FLEXIO_SHIFTCTL_TIMSEL(priv->config->timer) |
+ FLEXIO_SHIFTCTL_TIMPOL_PE |
+ FLEXIO_SHIFTCTL_PINCFG_OD |
+ FLEXIO_SHIFTCTL_PINSEL(priv->config->flexio_sda_pin) |
+ FLEXIO_SHIFTCTL_PINPOL_LO |
+ FLEXIO_SHIFTCTL_SMOD_TX,
+ S32K1XX_FLEXIO_SHIFTCTL0_OFFSET + priv->config->tx_inst * 0x4);
+
+ /* Start bit disabled and stop bit enabled
+ * (logic 0) for ACK/NACK detection..
+ */
+
+ flexio_putreg32(priv, FLEXIO_SHIFTCFG_INSRC_PIN |
+ FLEXIO_SHIFTCFG_SSTOP_ZERO |
+ FLEXIO_SHIFTCFG_SSTART_DIS,
+ S32K1XX_FLEXIO_SHIFTCFG0_OFFSET + priv->config->rx_inst * 0x4);
+
+ /* Configure receive using Timer X on falling edge
+ * of clock with input data on Pin X
+ */
+
+ flexio_putreg32(priv, FLEXIO_SHIFTCTL_TIMSEL(priv->config->timer) |
+ FLEXIO_SHIFTCTL_TIMPOL_NE |
+ FLEXIO_SHIFTCTL_PINCFG_DIS |
+ FLEXIO_SHIFTCTL_PINSEL(priv->config->flexio_sda_pin) |
+ FLEXIO_SHIFTCTL_SMOD_RX,
+ S32K1XX_FLEXIO_SHIFTCTL0_OFFSET + priv->config->rx_inst * 0x4);
+
+ /* Configure start bit, stop bit, enable on trigger high,
+ * disable on compare, reset if output equals pin.
+ * Initial clock state is logic 0 and is not affected by reset.
+ */
+
+ flexio_putreg32(priv, FLEXIO_TIMCFG_TSTART_ENA |
+ FLEXIO_TIMCFG_TSTOP_TIMDIS |
+ FLEXIO_TIMCFG_TIMENA_TRGHI |
+ FLEXIO_TIMCFG_TIMDIS_TIMCMP |
+ FLEXIO_TIMCFG_TIMRST_PINOUT |
+ FLEXIO_TIMCFG_TIMOUT_ZERO,
+ S32K1XX_FLEXIO_TIMCFG0_OFFSET + priv->config->two_word_timer * 0x4);
+
+ /* Configure dual 8-bit counter using Pin 1 output enable (SCL open drain),
+ * with Shifter 0 flag as the inverted trigger.
+ */
+
+ flexio_putreg32(priv, FLEXIO_TIMCTL_TRGSEL_SHIFTER(priv->config->tx_inst) |
+ FLEXIO_TIMCTL_TRGPOL_LO |
+ FLEXIO_TIMCTL_TRGSRC_INT |
+ FLEXIO_TIMCTL_PINCFG_OD |
+ FLEXIO_TIMCTL_PINSEL(priv->config->flexio_scl_pin) |
+ FLEXIO_TIMCTL_TIMOD_8BBAUD,
+ S32K1XX_FLEXIO_TIMCTL0_OFFSET + priv->config->two_word_timer * 0x4);
+
+ /* Configure 8-bit transfer. Set TIMCMP[15:0] = (number of bits x 2) - 1. */
+
+ flexio_putreg32(priv, 0xf, S32K1XX_FLEXIO_TIMCMP0_OFFSET +
+ priv->config->timer * 0x4);
+
+ /* Enable when Timer 0 is enabled, disable when Timer 0 is disabled,
+ * enable start bit and stop bit at end of each word,
+ * decrement on pin input..
+ */
+
+ flexio_putreg32(priv, FLEXIO_TIMCFG_TSTART_ENA |
+ FLEXIO_TIMCFG_TSTOP_TIMCMP |
+ FLEXIO_TIMCFG_TIMENA_TIMENA |
+ FLEXIO_TIMCFG_TIMDIS_TIMDIS |
+ FLEXIO_TIMCFG_TIMDEC_PINBOTHPIN,
+ S32K1XX_FLEXIO_TIMCFG0_OFFSET + priv->config->timer * 0x4);
+
+ /* Configure 16-bit counter using inverted Pin 1 input (SCL).. */
+
+ flexio_putreg32(priv, FLEXIO_TIMCTL_TRGSEL_SHIFTER(priv->config->tx_inst) |
+ FLEXIO_TIMCTL_TRGPOL_LO |
+ FLEXIO_TIMCTL_TRGSRC_INT |
+ FLEXIO_TIMCTL_PINSEL(priv->config->flexio_scl_pin) |
+ FLEXIO_TIMCTL_PINPOL_LO |
+ FLEXIO_TIMCTL_TIMOD_16BCNT,
+ S32K1XX_FLEXIO_TIMCTL0_OFFSET + priv->config->timer * 0x4);
+
+ flexio_modifyreg32(priv, S32K1XX_FLEXIO_CTRL_OFFSET, 0,
+ FLEXIO_CTRL_FLEXEN);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: s32k1xx_flexio_i2cbus_initialize
+ *
+ * Description:
+ * Initialise an I2C device
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *s32k1xx_flexio_i2cbus_initialize(int port)
+{
+ struct s32k1xx_flexio_i2cdev_s *priv;
+
+ if (port == 0)
+ {
+ priv = &g_i2c0dev;
+ priv->dev.ops = &s32k1xx_flexio_i2c_ops;
+ }
+ else
+ {
+ i2cerr("I2C Only support 0\n");
+ return NULL;
+ }
+
+ nxmutex_lock(&priv->lock);
+
+ /* Test if already initialized or not */
+
+ if (1 < ++priv->refs)
+ {
+ nxmutex_unlock(&priv->lock);
+ return &priv->dev;
+ }
+
+ priv->port = port;
+ priv->frequency = 0;
+
+ s32k1xx_get_pclkfreq(FLEXIO0_CLK, &priv->base_freq);
+
+ /* Configure pins */
+
+ s32k1xx_pinconfig(priv->config->scl_pin);
+ s32k1xx_pinconfig(priv->config->sda_pin);
+
+ s32k1xx_flexio_i2c_init(priv);
+
+ /* Attach Interrupt Handler */
+
+ irq_attach(priv->irqid, s32k1xx_flexio_i2c_interrupt, priv);
+
+ /* Enable Interrupt Handler */
+
+ up_enable_irq(priv->irqid);
+
+ nxmutex_unlock(&priv->lock);
+ return &priv->dev;
+}
+
+/****************************************************************************
+ * Name: s32k1xx_flexio_i2cbus_uninitialize
+ *
+ * Description:
+ * Uninitialise an I2C device
+ *
+ ****************************************************************************/
+
+int s32k1xx_flexio_i2cbus_uninitialize(struct i2c_master_s *dev)
+{
+ struct s32k1xx_flexio_i2cdev_s *priv =
+ (struct s32k1xx_flexio_i2cdev_s *)dev;
+
+ /* Decrement reference count and check for underflow */
+
+ if (priv->refs == 0)
+ {
+ return ERROR;
+ }
+
+ nxmutex_lock(&priv->lock);
+ if (--priv->refs)
+ {
+ nxmutex_unlock(&priv->lock);
+ return OK;
+ }
+
+ up_disable_irq(priv->irqid);
+ irq_detach(priv->irqid);
+
+ nxmutex_unlock(&priv->lock);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: s32k1xx_flexio_i2c_transfer
+ *
+ * Description:
+ * Perform a sequence of I2C transfers
+ *
+ * TODO: Multiple i2c_msg_s read operations with the same address are not
+ * currently guaranteed.
+ ****************************************************************************/
+
+static int s32k1xx_flexio_i2c_transfer(struct i2c_master_s *dev,
+ struct i2c_msg_s *msgs, int count)
+{
+ struct s32k1xx_flexio_i2cdev_s *priv =
+ (struct s32k1xx_flexio_i2cdev_s *)dev;
+ int i;
+ int ret = 0;
+ int semval = 0;
+ struct timespec abs_time;
+
+ if (msgs->frequency > I2C_MAX_FREQUENCY)
+ {
+ return -EINVAL;
+ }
+
+ DEBUGASSERT(dev != NULL);
+
+ /* Get exclusive access to the I2C bus */
+
+ nxmutex_lock(&priv->lock);
+
+ /* Check wait semaphore value. If the value is not 0, the transfer can not
+ * be performed normally.
+ */
+
+ ret = nxsem_get_value(&priv->wait, &semval);
+ DEBUGASSERT(ret == OK && semval == 0);
+
+ priv->total_bytes = 0;
+
+ for (i = 0; i < count; i++)
+ {
+ priv->total_bytes += msgs[i].length;
+ }
+
+ /* FlexIO driver can only handle transfers of maximum 12 bytes */
+
+ if (priv->total_bytes <= 12)
+ {
+ /* Pass msg descriptor via device context */
+
+ priv->msgs = msgs;
+ priv->error = OK;
+ set_expiretime(50, &abs_time);
+
+ s32k1xx_flexio_i2c_set_freq_and_size(priv, msgs->frequency,
+ priv->total_bytes);
+ s32k1xx_flexio_i2c_set_nack(priv);
+ s32k1xx_flexio_i2c_clear_errors(priv);
+
+ priv->tx_done = 0;
+ priv->tx_count = 0;
+
+ if (msgs->flags & I2C_M_READ)
+ {
+ priv->reg_buff_offset = -1; /* Ignore readback addr */
+ priv->state = INTSTATE_RECV_DATA;
+ flexio_putreg32(priv, FLEXIO_ADDR_READ(msgs->addr),
+ S32K1XX_FLEXIO_SHIFTBUFBBS0_OFFSET +
+ priv->config->tx_inst * 0x4);
+ }
+ else
+ {
+ priv->reg_buff_offset = 0;
+ priv->reg_buff_mod = 0;
+ priv->state = INTSTATE_WRITE_DATA;
+ flexio_putreg32(priv, FLEXIO_ADDR_WRITE(msgs->addr),
+ S32K1XX_FLEXIO_SHIFTBUFBBS0_OFFSET +
+ priv->config->tx_inst * 0x4);
+ }
+
+ enable_shifter_status_interrupts(priv, FLEXIO_TX_IRQ | FLEXIO_RX_IRQ);
+ nxsem_timedwait_uninterruptible(&priv->wait, &abs_time);
+
+ if (priv->error != OK)
+ {
+ ret = priv->error;
+ }
+ }
+ else
+ {
+ /* FlexIO driver can only handle transfers of maximum 12 bytes */
+
+ ret = -EINVAL;
+ }
+
+ nxmutex_unlock(&priv->lock);
+ return ret;
+}
+
+#endif /* CONFIG_S32K1XX_FLEXIO_I2C */
diff --git a/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.h b/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.h
new file mode 100644
index 0000000..d8b4f0e
--- /dev/null
+++ b/arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+ * arch/arm/src/s32k1xx/s32k1xx_flexio_i2c.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_S32K1XX_S32K1XX_FLEXIO_I2C_H
+#define __ARCH_ARM_SRC_S32K1XX_S32K1XX_FLEXIO_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include "chip.h"
+#include "hardware/s32k1xx_flexio.h"
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: s32k1xx_i2cbus_initialize
+ *
+ * Description:
+ * Initialize the selected I2C port. And return a unique instance of struct
+ * struct i2c_master_s. This function may be called to obtain multiple
+ * instances of the interface, each of which may be set up with a
+ * different frequency and slave address.
+ *
+ * Input Parameters:
+ * Port number (for hardware that has multiple I2C interfaces)
+ *
+ * Returned Value:
+ * Valid I2C device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *s32k1xx_flexio_i2cbus_initialize(int port);
+
+/****************************************************************************
+ * Name: s32k1xx_i2cbus_uninitialize
+ *
+ * Description:
+ * De-initialize the selected I2C port, and power down the device.
+ *
+ * Input Parameters:
+ * Device structure as returned by the s32k1xx_i2cbus_initialize()
+ *
+ * Returned Value:
+ * OK on success, ERROR when internal reference count mismatch or dev
+ * points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int s32k1xx_flexio_i2cbus_uninitialize(struct i2c_master_s *dev);
+
+#endif /* __ARCH_ARM_SRC_S32K1XX_S32K1XX_FLEXIO_I2C_H */