text: /**
   * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
   *
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form, except as embedded into a Nordic
   *    Semiconductor ASA integrated circuit in a product or a software update for
   *    such product, must reproduce the above copyright notice, this list of
   *    conditions and the following disclaimer in the documentation and/or other
   *    materials provided with the distribution.
   *
   * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
   *    contributors may be used to endorse or promote products derived from this
   *    software without specific prior written permission.
   *
   * 4. This software, with or without modification, must only be used with a
   *    Nordic Semiconductor ASA integrated circuit.
   *
   * 5. Any software provided in binary form under this license must not be reverse
   *    engineered, decompiled, modified and/or disassembled.
   *
   * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   */
  
  #include <nrfx.h>
  
  #if NRFX_CHECK(NRFX_TWIS_ENABLED)
  
  #if !(NRFX_CHECK(NRFX_TWIS0_ENABLED) || NRFX_CHECK(NRFX_TWIS1_ENABLED))
  #error "No enabled TWIS instances. Check <nrfx_config.h>."
  #endif
  
  #include <nrfx_twis.h>
  #include "prs/nrfx_prs.h"
  
  #define NRFX_LOG_MODULE TWIS
  #include <nrfx_log.h>
  
  #define EVT_TO_STR(event)                                             \
      (event == NRF_TWIS_EVENT_STOPPED   ? "NRF_TWIS_EVENT_STOPPED"   : \
      (event == NRF_TWIS_EVENT_ERROR     ? "NRF_TWIS_EVENT_ERROR"     : \
      (event == NRF_TWIS_EVENT_RXSTARTED ? "NRF_TWIS_EVENT_RXSTARTED" : \
      (event == NRF_TWIS_EVENT_TXSTARTED ? "NRF_TWIS_EVENT_TXSTARTED" : \
      (event == NRF_TWIS_EVENT_WRITE     ? "NRF_TWIS_EVENT_WRITE"     : \
      (event == NRF_TWIS_EVENT_READ      ? "NRF_TWIS_EVENT_READ"      : \
                                           "UNKNOWN EVENT"))))))
  
  
  /**
   * @brief Actual state of internal state machine
   *
   * Current substate of powered on state.
   */
  typedef enum
  {
      NRFX_TWIS_SUBSTATE_IDLE,          ///< No ongoing transmission
      NRFX_TWIS_SUBSTATE_READ_WAITING,  ///< Read request received, waiting for data
      NRFX_TWIS_SUBSTATE_READ_PENDING,  ///< Reading is actually pending (data sending)
      NRFX_TWIS_SUBSTATE_WRITE_WAITING, ///< Write request received, waiting for data buffer
      NRFX_TWIS_SUBSTATE_WRITE_PENDING, ///< Writing is actually pending (data receiving)
  } nrfx_twis_substate_t;
  
  // Control block - driver instance local data.
  typedef struct
  {
      nrfx_twis_event_handler_t       ev_handler;
      // Internal copy of hardware errors flags merged with specific internal
      // driver errors flags.
      // This value can be changed in the interrupt and cleared in the main program.
      // Always use Atomic load-store when updating this value in main loop.
      volatile uint32_t               error;
      nrfx_drv_state_t                state;
      volatile nrfx_twis_substate_t   substate;
  
      volatile bool                   semaphore;
  } twis_control_block_t;
  static twis_control_block_t m_cb[NRFX_TWIS_ENABLED_COUNT];
  
  /**
   * @brief Used interrupts mask
   *
   * Mask for all interrupts used by this library
   */
  static const uint32_t m_used_ints_mask = NRF_TWIS_INT_STOPPED_MASK   |
                                           NRF_TWIS_INT_ERROR_MASK     |
                                           NRF_TWIS_INT_RXSTARTED_MASK |
                                           NRF_TWIS_INT_TXSTARTED_MASK |
                                           NRF_TWIS_INT_WRITE_MASK     |
                                           NRF_TWIS_INT_READ_MASK;
  
  /**
   * @brief Clear all  events
   *
   * Function clears all actually pending events
   */
  static void nrfx_twis_clear_all_events(NRF_TWIS_Type * const p_reg)
  {
      /* Clear all events */
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_STOPPED);
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_ERROR);
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_RXSTARTED);
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_TXSTARTED);
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_WRITE);
      nrf_twis_event_clear(p_reg, NRF_TWIS_EVENT_READ);
  }
  
  /**
   * @brief Reset all the registers to known state
   *
   * This function clears all registers that requires it to known state.
   * TWIS is left disabled after this function.
   * All events are cleared.
   * @param[out] p_reg TWIS to reset register address
   */
  static inline void nrfx_twis_swreset(NRF_TWIS_Type * p_reg)
  {
      /* Disable TWIS */
      nrf_twis_disable(p_reg);
  
      /* Disconnect pins */
      nrf_twis_pins_set(p_reg, ~0U, ~0U);
  
      /* Disable interrupt global for the instance */
      NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg));
  
      /* Disable interrupts */
      nrf_twis_int_disable(p_reg, ~0U);
  }
  
  /**
   * @brief Configure pin
   *
   * Function configures selected for work as SDA or SCL.
   * @param pin Pin number to configure
   */
  static inline void nrfx_twis_config_pin(uint32_t pin, nrf_gpio_pin_pull_t pull)
  {
      nrf_gpio_cfg(pin,
                   NRF_GPIO_PIN_DIR_INPUT,
                   NRF_GPIO_PIN_INPUT_DISCONNECT,
                   pull,
                   NRF_GPIO_PIN_S0D1,
                   NRF_GPIO_PIN_NOSENSE);
  }
  
  /**
   * @brief Auxiliary function for getting event state on right bit possition
   *
   * This function calls @ref nrf_twis_event_get function but the the result
   * is shifted to match INTEN register scheme.
   *
   * @param[in,out] p_reg TWIS to read  event from
   * @param ev  Event code
   *
   * @return Selected event state shifted by @ref nrfx_event_to_bitpos
   *
   * @sa nrf_twis_event_get
   * @sa nrfx_event_to_bitpos
   */
  static inline uint32_t nrfx_twis_event_bit_get(NRF_TWIS_Type *  p_reg,
                                                 nrf_twis_event_t ev)
  {
      return (uint32_t)nrf_twis_event_get_and_clear(p_reg, ev) << nrfx_event_to_bitpos(ev);
  }
  
  /**
   * @brief Auxiliary function for checking event bit inside given flags value
   *
   * Function used here to check presence of the event inside given flags value.
   * It transforms given event to bit possition and then checks if in given variable it is cleared.
   *
   * @param flags Flags to test
   * @param ev Event code
   *
   * @retval true Flag for selected event is set
   * @retval false Flag for selected event is cleared
   */
  static inline bool nrfx_twis_check_bit(uint32_t         flags,
                                         nrf_twis_event_t ev)
  {
      return 0 != (flags & (1U << nrfx_event_to_bitpos(ev)));
  }
  
  /**
   * @brief Auxiliary function for clearing event bit in given flags value
   *
   * Function used to clear selected event bit.
   *
   * @param flags Flags to process
   * @param ev    Event code to clear
   *
   * @return Value @em flags with cleared event bit that matches given @em ev
   */
  static inline uint32_t nrfx_twis_clear_bit(uint32_t         flags,
                                             nrf_twis_event_t ev)
  {
      return flags & ~(1U << nrfx_event_to_bitpos(ev));
  }
  
  static void call_event_handler(twis_control_block_t const * p_cb,
                                 nrfx_twis_evt_t const *      p_evt)
  {
      nrfx_twis_event_handler_t handler = p_cb->ev_handler;
      if (handler != NULL)
      {
          handler(p_evt);
      }
  }
  
  /**
   * @brief Auxiliary function for error processing
   *
   * Function called when in current substate the event apears and it cannot be processed.
   * It should be called also on ERROR event.
   * If given @em error parameter has zero value the @ref NRFX_TWIS_ERROR_UNEXPECTED_EVENT
   * would be set.
   *
   * @param p_cb   Pointer to the driver instance control block.
   * @param evt    What error event raport to event handler
   * @param error  Error flags
   */
  static inline void nrfx_twis_process_error(twis_control_block_t * p_cb,
                                             nrfx_twis_evt_type_t   evt,
                                             uint32_t               error)
  {
      if (0 == error)
      {
          error = NRFX_TWIS_ERROR_UNEXPECTED_EVENT;
      }
      nrfx_twis_evt_t evdata;
      evdata.type       = evt;
      evdata.data.error = error;
  
      p_cb->error |= error;
  
      call_event_handler(p_cb, &evdata);
  }
  
  static void nrfx_twis_state_machine(NRF_TWIS_Type *        p_reg,
                                      twis_control_block_t * p_cb)
  {
      if (!NRFX_TWIS_NO_SYNC_MODE)
      {
          /* Exclude parallel processing of this function */
          if (p_cb->semaphore)
          {
              return;
          }
          p_cb->semaphore = 1;
      }
  
      /* Event data structure to be passed into event handler */
      nrfx_twis_evt_t evdata;
      /* Current substate copy  */
      nrfx_twis_substate_t substate = p_cb->substate;
      /* Event flags */
      uint32_t ev = 0;
  
      /* Get all events */
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_STOPPED);
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_ERROR);
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_RXSTARTED);
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_TXSTARTED);
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_WRITE);
      ev |= nrfx_twis_event_bit_get(p_reg, NRF_TWIS_EVENT_READ);
  
      /* State machine */
      while (0 != ev)
      {
          switch (substate)
          {
          case NRFX_TWIS_SUBSTATE_IDLE:
              if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
              {
                  /* Stopped event is always allowed in IDLE state - just ignore */
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
              }
              else if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ))
              {
                  evdata.type = NRFX_TWIS_EVT_READ_REQ;
                  if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_TXSTARTED))
                  {
                      substate = NRFX_TWIS_SUBSTATE_READ_PENDING;
                      evdata.data.buf_req = false;
                  }
                  else
                  {
                      substate = NRFX_TWIS_SUBSTATE_READ_WAITING;
                      evdata.data.buf_req = true;
                  }
                  call_event_handler(p_cb, &evdata);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_READ);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_WRITE);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
              }
              else if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE))
              {
                  evdata.type = NRFX_TWIS_EVT_WRITE_REQ;
                  if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_RXSTARTED))
                  {
                      substate = NRFX_TWIS_SUBSTATE_WRITE_PENDING;
                      evdata.data.buf_req = false;
                  }
                  else
                  {
                      substate = NRFX_TWIS_SUBSTATE_WRITE_WAITING;
                      evdata.data.buf_req = true;
                  }
                  call_event_handler(p_cb, &evdata);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_READ);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_WRITE);
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
              }
              else
              {
                  nrfx_twis_process_error(p_cb,
                                          NRFX_TWIS_EVT_GENERAL_ERROR,
                                          nrf_twis_error_source_get_and_clear(p_reg));
                  ev = 0;
              }
              break;
          case NRFX_TWIS_SUBSTATE_READ_WAITING:
              if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_TXSTARTED) ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE)     ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ)      ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
              {
                  substate = NRFX_TWIS_SUBSTATE_READ_PENDING;
                  /* Any other bits requires further processing in PENDING substate */
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_TXSTARTED);
              }
              else
              {
                  nrfx_twis_process_error(p_cb,
                                          NRFX_TWIS_EVT_READ_ERROR,
                                          nrf_twis_error_source_get_and_clear(p_reg));
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = 0;
              }
              break;
          case NRFX_TWIS_SUBSTATE_READ_PENDING:
              if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ)  ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
              {
                  evdata.type = NRFX_TWIS_EVT_READ_DONE;
                  evdata.data.tx_amount = nrf_twis_tx_amount_get(p_reg);
                  NRFX_LOG_INFO("Transfer tx_len:%d", evdata.data.tx_amount);
                  NRFX_LOG_DEBUG("Tx data:");
                  NRFX_LOG_HEXDUMP_DEBUG((uint8_t const *)p_reg->TXD.PTR,
                                         evdata.data.tx_amount * sizeof(uint8_t));
                  call_event_handler(p_cb, &evdata);
                  /* Go to idle and repeat the state machine if READ or WRITE events detected.
                   * This time READ or WRITE would be started */
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
              }
              else
              {
                  nrfx_twis_process_error(p_cb,
                                          NRFX_TWIS_EVT_READ_ERROR,
                                          nrf_twis_error_source_get_and_clear(p_reg));
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = 0;
              }
              break;
          case NRFX_TWIS_SUBSTATE_WRITE_WAITING:
              if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_RXSTARTED) ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE)     ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ)      ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
              {
                  substate = NRFX_TWIS_SUBSTATE_WRITE_PENDING;
                  /* Any other bits requires further processing in PENDING substate */
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_RXSTARTED);
              }
              else
              {
                  nrfx_twis_process_error(p_cb,
                                          NRFX_TWIS_EVT_WRITE_ERROR,
                                          nrf_twis_error_source_get_and_clear(p_reg));
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = 0;
              }
              break;
          case NRFX_TWIS_SUBSTATE_WRITE_PENDING:
              if (nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_WRITE) ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_READ)  ||
                  nrfx_twis_check_bit(ev, NRF_TWIS_EVENT_STOPPED))
              {
                  evdata.type = NRFX_TWIS_EVT_WRITE_DONE;
                  evdata.data.rx_amount = nrf_twis_rx_amount_get(p_reg);
                  call_event_handler(p_cb, &evdata);
                  /* Go to idle and repeat the state machine if READ or WRITE events detected.
                   * This time READ or WRITE would be started */
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = nrfx_twis_clear_bit(ev, NRF_TWIS_EVENT_STOPPED);
              }
              else
              {
                  nrfx_twis_process_error(p_cb,
                                          NRFX_TWIS_EVT_WRITE_ERROR,
                                          nrf_twis_error_source_get_and_clear(p_reg));
                  substate = NRFX_TWIS_SUBSTATE_IDLE;
                  ev = 0;
              }
              break;
          default:
              substate = NRFX_TWIS_SUBSTATE_IDLE;
              /* Do not clear any events and repeat the machine */
              break;
          }
      }
  
      p_cb->substate = substate;
      if (!NRFX_TWIS_NO_SYNC_MODE)
      {
          p_cb->semaphore = 0;
      }
  }
  
  
  static inline void nrfx_twis_preprocess_status(nrfx_twis_t const * p_instance)
  {
      if (!NRFX_TWIS_NO_SYNC_MODE)
      {
          NRF_TWIS_Type *        p_reg = p_instance->p_reg;
          twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
          if (NULL == p_cb->ev_handler)
          {
              nrfx_twis_state_machine(p_reg, p_cb);
          }
      }
  }
  
  
  /* -------------------------------------------------------------------------
   * Implementation of interface functions
   *
   */
  
  
  nrfx_err_t nrfx_twis_init(nrfx_twis_t const *        p_instance,
                            nrfx_twis_config_t const * p_config,
                            nrfx_twis_event_handler_t  event_handler)
  {
      NRFX_ASSERT(p_config);
      NRFX_ASSERT(p_config->scl != p_config->sda);
      nrfx_err_t err_code;
  
      NRF_TWIS_Type *        p_reg = p_instance->p_reg;
      twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
  
      if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
      {
          err_code = NRFX_ERROR_INVALID_STATE;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
  
  #if NRFX_CHECK(NRFX_PRS_ENABLED)
      static nrfx_irq_handler_t const irq_handlers[NRFX_TWIS_ENABLED_COUNT] = {
          #if NRFX_CHECK(NRFX_TWIS0_ENABLED)
          nrfx_twis_0_irq_handler,
          #endif
          #if NRFX_CHECK(NRFX_TWIS1_ENABLED)
          nrfx_twis_1_irq_handler,
          #endif
      };
      if (nrfx_prs_acquire(p_reg,
              irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
      {
          err_code = NRFX_ERROR_BUSY;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
  #endif // NRFX_CHECK(NRFX_PRS_ENABLED)
  
      if (!NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY)
      {
          nrfx_twis_swreset(p_reg);
      }
  
      nrfx_twis_config_pin(p_config->scl, p_config->scl_pull);
      nrfx_twis_config_pin(p_config->sda, p_config->sda_pull);
  
      nrf_twis_config_addr_mask_t addr_mask = (nrf_twis_config_addr_mask_t)0;
      if (0 == (p_config->addr[0] | p_config->addr[1]))
      {
          addr_mask = NRF_TWIS_CONFIG_ADDRESS0_MASK;
      }
      else
      {
          if (0 != p_config->addr[0])
          {
              addr_mask |= NRF_TWIS_CONFIG_ADDRESS0_MASK;
          }
          if (0 != p_config->addr[1])
          {
              addr_mask |= NRF_TWIS_CONFIG_ADDRESS1_MASK;
          }
      }
  
      /* Peripheral interrupt configure
       * (note - interrupts still needs to be configured in INTEN register.
       * This is done in enable function) */
      NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_reg),
                            p_config->interrupt_priority);
      NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg));
  
      /* Configure */
      nrf_twis_pins_set          (p_reg, p_config->scl, p_config->sda);
      nrf_twis_address_set       (p_reg, 0, p_config->addr[0]);
      nrf_twis_address_set       (p_reg, 1, p_config->addr[1]);
      nrf_twis_config_address_set(p_reg, addr_mask);
  
      /* Clear semaphore */
      if (!NRFX_TWIS_NO_SYNC_MODE)
      {
          p_cb->semaphore = 0;
      }
      /* Set internal instance variables */
      p_cb->substate   = NRFX_TWIS_SUBSTATE_IDLE;
      p_cb->ev_handler = event_handler;
      p_cb->state      = NRFX_DRV_STATE_INITIALIZED;
      err_code = NRFX_SUCCESS;
      NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
      return err_code;
  }
  
  
  void nrfx_twis_uninit(nrfx_twis_t const * p_instance)
  {
      NRF_TWIS_Type *        p_reg = p_instance->p_reg;
      twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
      NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
  
      TWIS_PSEL_Type psel = p_reg->PSEL;
  
      nrfx_twis_swreset(p_reg);
  
      /* Clear pins state if */
      if (!(TWIS_PSEL_SCL_CONNECT_Msk & psel.SCL))
      {
          nrf_gpio_cfg_default(psel.SCL);
      }
      if (!(TWIS_PSEL_SDA_CONNECT_Msk & psel.SDA))
      {
          nrf_gpio_cfg_default(psel.SDA);
      }
  
  #if NRFX_CHECK(NRFX_PRS_ENABLED)
      nrfx_prs_release(p_reg);
  #endif
  
      /* Clear variables */
      p_cb->ev_handler = NULL;
      p_cb->state      = NRFX_DRV_STATE_UNINITIALIZED;
  }
  
  
  void nrfx_twis_enable(nrfx_twis_t const * p_instance)
  {
      NRF_TWIS_Type *        p_reg = p_instance->p_reg;
      twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
      NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
  
      nrfx_twis_clear_all_events(p_reg);
  
      /* Enable interrupts */
      if (NULL != p_cb->ev_handler)
      {
          nrf_twis_int_enable(p_reg, m_used_ints_mask);
      }
  
      nrf_twis_enable(p_reg);
      p_cb->error    = 0;
      p_cb->state    = NRFX_DRV_STATE_POWERED_ON;
      p_cb->substate = NRFX_TWIS_SUBSTATE_IDLE;
  }
  
  
  void nrfx_twis_disable(nrfx_twis_t const * p_instance)
  {
      NRF_TWIS_Type *        p_reg = p_instance->p_reg;
      twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
      NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
  
      nrf_twis_int_disable(p_reg, m_used_ints_mask);
  
      nrf_twis_disable(p_reg);
      p_cb->state = NRFX_DRV_STATE_INITIALIZED;
  }
  
  /* ARM recommends not using the LDREX and STREX instructions in C code.
   * This is because the compiler might generate loads and stores between
   * LDREX and STREX, potentially clearing the exclusive monitor set by LDREX.
   * This recommendation also applies to the byte, halfword, and doubleword
   * variants LDREXB, STREXB, LDREXH, STREXH, LDREXD, and STREXD.
   *
   * This is the reason for the function below to be implemented in assembly.
   */
  //lint -save -e578
  #if defined (__CC_ARM )
  static __ASM uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
  {
      mov   r3, r0
      mov   r1, #0
  nrfx_twis_error_get_and_clear_internal_try
      ldrex r0, [r3]
      strex r2, r1, [r3]
      cmp   r2, r1                                     /* did this succeed?       */
      bne   nrfx_twis_error_get_and_clear_internal_try /* no - try again          */
      bx    lr
  }
  #elif defined ( __GNUC__ )
  static uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
  {
      uint32_t ret;
      uint32_t temp;
      __ASM volatile(
          "   .syntax unified           \n"
          "nrfx_twis_error_get_and_clear_internal_try:         \n"
          "   ldrex %[ret], [%[perror]]                        \n"
          "   strex %[temp], %[zero], [%[perror]]              \n"
          "   cmp   %[temp], %[zero]                           \n"
          "   bne   nrfx_twis_error_get_and_clear_internal_try \n"
      : /* Output */
          [ret]"=&l"(ret),
          [temp]"=&l"(temp)
      : /* Input */
          [zero]"l"(0),
          [perror]"l"(perror)
      );
      (void)temp;
      return ret;
  }
  #elif defined ( __ICCARM__ )
  static uint32_t nrfx_twis_error_get_and_clear_internal(uint32_t volatile * perror)
  {
      uint32_t ret;
      uint32_t temp;
      __ASM volatile(
          "1:         \n"
          "   ldrex %[ret], [%[perror]]                           \n"
          "   strex %[temp], %[zero], [%[perror]]                 \n"
          "   cmp   %[temp], %[zero]                              \n"
          "   bne.n 1b \n"
      : /* Output */
          [ret]"=&l"(ret),
          [temp]"=&l"(temp)
      : /* Input */
          [zero]"l"(0),
          [perror]"l"(perror)
      );
      (void)temp;
      return ret;
  }
  #else
      #error Unknown compiler
  #endif
  //lint -restore
  
  uint32_t nrfx_twis_error_get_and_clear(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      /* Make sure that access to error member is atomic
       * so there is no bit that is cleared if it is not copied to local variable already. */
      twis_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];
      return nrfx_twis_error_get_and_clear_internal(&p_cb->error);
  }
  
  
  nrfx_err_t nrfx_twis_tx_prepare(nrfx_twis_t const * p_instance,
                                  void const *        p_buf,
                                  size_t              size)
  {
      nrfx_err_t err_code;
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
  
      /* Check power state*/
      if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
      {
          err_code = NRFX_ERROR_INVALID_STATE;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
      /* Check data address */
      if (!nrfx_is_in_ram(p_buf))
      {
          err_code = NRFX_ERROR_INVALID_ADDR;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
      /* Check data size */
      if ((size & TWIS_TXD_MAXCNT_MAXCNT_Msk) != size)
      {
          err_code = NRFX_ERROR_INVALID_LENGTH;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
  
      nrf_twis_tx_prepare(p_instance->p_reg,
                          (uint8_t const *)p_buf,
                          (nrf_twis_amount_t)size);
      err_code = NRFX_SUCCESS;
      NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
      return err_code;
  }
  
  
  nrfx_err_t nrfx_twis_rx_prepare(nrfx_twis_t const * p_instance,
                                  void *              p_buf,
                                  size_t              size)
  {
      nrfx_err_t err_code;
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
  
      /* Check power state*/
      if (p_cb->state != NRFX_DRV_STATE_POWERED_ON)
      {
          err_code = NRFX_ERROR_INVALID_STATE;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
      /* Check data address */
      if (!nrfx_is_in_ram(p_buf))
      {
          err_code = NRFX_ERROR_INVALID_ADDR;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
      /* Check data size */
      if ((size & TWIS_RXD_MAXCNT_MAXCNT_Msk) != size)
      {
          err_code = NRFX_ERROR_INVALID_LENGTH;
          NRFX_LOG_WARNING("Function: %s, error code: %s.",
                           __func__,
                           NRFX_LOG_ERROR_STRING_GET(err_code));
          return err_code;
      }
  
      nrf_twis_rx_prepare(p_instance->p_reg,
                          (uint8_t *)p_buf,
                          (nrf_twis_amount_t)size);
      err_code = NRFX_SUCCESS;
      NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
      return err_code;
  }
  
  
  bool nrfx_twis_is_busy(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
      return NRFX_TWIS_SUBSTATE_IDLE != p_cb->substate;
  }
  
  bool nrfx_twis_is_waiting_tx_buff(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
      return NRFX_TWIS_SUBSTATE_READ_WAITING == p_cb->substate;
  }
  
  bool nrfx_twis_is_waiting_rx_buff(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
      return NRFX_TWIS_SUBSTATE_WRITE_WAITING == p_cb->substate;
  }
  
  bool nrfx_twis_is_pending_tx(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
      return NRFX_TWIS_SUBSTATE_READ_PENDING == p_cb->substate;
  }
  
  bool nrfx_twis_is_pending_rx(nrfx_twis_t const * p_instance)
  {
      nrfx_twis_preprocess_status(p_instance);
      twis_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
      return NRFX_TWIS_SUBSTATE_WRITE_PENDING == p_cb->substate;
  }
  
  
  #if NRFX_CHECK(NRFX_TWIS0_ENABLED)
  void nrfx_twis_0_irq_handler(void)
  {
      nrfx_twis_state_machine(NRF_TWIS0, &m_cb[NRFX_TWIS0_INST_IDX]);
  }
  #endif
  
  #if NRFX_CHECK(NRFX_TWIS1_ENABLED)
  void nrfx_twis_1_irq_handler(void)
  {
      nrfx_twis_state_machine(NRF_TWIS1, &m_cb[NRFX_TWIS1_INST_IDX]);
  }
  #endif
  
  #endif // NRFX_CHECK(NRFX_TWIS_ENABLED)
  