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 */