/****************************************************************************
 * boards/arm/cxd56xx/drivers/sensors/bmp280_scu.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 <stdlib.h>
#include <stdio.h>
#include <fixedmath.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/sensors/bmp280.h>
#include <arch/chip/scu.h>

#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_BMP280_SCU)

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifdef CONFIG_SENSORS_BMI280_SCU_DECI_PRESS
#  define PRESS_SEQ_TYPE SEQ_TYPE_DECI
#else
#  define PRESS_SEQ_TYPE SEQ_TYPE_NORMAL
#endif
#ifdef CONFIG_SENSORS_BMI280_SCU_DECI_TEMP
#  define TEMP_SEQ_TYPE SEQ_TYPE_DECI
#else
#  define TEMP_SEQ_TYPE SEQ_TYPE_NORMAL
#endif

#define BMP280_ADDR         0x76
#define BMP280_FREQ         400000
#define DEVID               0x58

#define BMP280_DIG_T1_LSB   0x88
#define BMP280_DIG_T1_MSB   0x89
#define BMP280_DIG_T2_LSB   0x8a
#define BMP280_DIG_T2_MSB   0x8b
#define BMP280_DIG_T3_LSB   0x8c
#define BMP280_DIG_T3_MSB   0x8d
#define BMP280_DIG_P1_LSB   0x8e
#define BMP280_DIG_P1_MSB   0x8f
#define BMP280_DIG_P2_LSB   0x90
#define BMP280_DIG_P2_MSB   0x91
#define BMP280_DIG_P3_LSB   0x92
#define BMP280_DIG_P3_MSB   0x93
#define BMP280_DIG_P4_LSB   0x94
#define BMP280_DIG_P4_MSB   0x95
#define BMP280_DIG_P5_LSB   0x96
#define BMP280_DIG_P5_MSB   0x97
#define BMP280_DIG_P6_LSB   0x98
#define BMP280_DIG_P6_MSB   0x99
#define BMP280_DIG_P7_LSB   0x9a
#define BMP280_DIG_P7_MSB   0x9b
#define BMP280_DIG_P8_LSB   0x9c
#define BMP280_DIG_P8_MSB   0x9d
#define BMP280_DIG_P9_LSB   0x9e
#define BMP280_DIG_P9_MSB   0x9f

#define BMP280_DEVID        0xd0
#define BMP280_SOFT_RESET   0xe0
#define BMP280_STAT         0xf3
#define BMP280_CTRL_MEAS    0xf4
#define BMP280_CONFIG       0xf5
#define BMP280_PRESS_MSB    0xf7
#define BMP280_PRESS_LSB    0xf8
#define BMP280_PRESS_XLSB   0xf9
#define BMP280_TEMP_MSB     0xfa
#define BMP280_TEMP_LSB     0xfb
#define BMP280_TEMP_XLSB    0xfc

/* power mode */

#define BMP280_SLEEP_MODE   0x00
#define BMP280_FORCED_MODE  0x01
#define BMP280_NORMAL_MODE  0x03

/* oversampling */

#define BMP280_OVERSAMP_SKIPPED 0x00
#define BMP280_OVERSAMP_1X      0x01
#define BMP280_OVERSAMP_2X      0x02
#define BMP280_OVERSAMP_4X      0x03
#define BMP280_OVERSAMP_8X      0x04
#define BMP280_OVERSAMP_16X     0x05

/* BMP280 have press and temp, pressure respectively in 24 bits.
 */

#define BMP280PRESS_BYTESPERSAMPLE  3
#define BMP280PRESS_ELEMENTSIZE     3
#define BMP280TEMP_BYTESPERSAMPLE   3
#define BMP280TEMP_ELEMENTSIZE      3

#ifndef itemsof
#  define itemsof(array) (sizeof(array)/sizeof(array[0]))
#endif

/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/

struct bmp280_dev_s
{
  struct i2c_master_s *i2c; /* I2C interface */
  uint8_t addr;             /* BMP280 I2C address */
  int freq;                 /* BMP280 Frequency <= 3.4MHz */
  int port;                 /* I2C port */
  struct seq_s *seq;        /* Sequencer */
  int id;                   /* Sequencer id */
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static uint8_t bmp280_getreg8(struct bmp280_dev_s *priv,
                              uint8_t regaddr);
static void bmp280_putreg8(struct bmp280_dev_s *priv,
                           uint8_t regaddr,
                           uint8_t regval);

/* Character driver methods */

static int bmp280_open_press(struct file *filep);
static int bmp280_open_temp(struct file *filep);
static int bmp280_close_press(struct file *filep);
static int bmp280_close_temp(struct file *filep);
static ssize_t bmp280_read_press(struct file *filep, char *buffer,
                                 size_t buflen);
static ssize_t bmp280_read_temp(struct file *filep, char *buffer,
                                size_t buflen);
static ssize_t bmp280_write(struct file *filep, const char *buffer,
                            size_t buflen);
static int bmp280_ioctl_press(struct file *filep, int cmd,
                              unsigned long arg);
static int bmp280_ioctl_temp(struct file *filep, int cmd,
                             unsigned long arg);

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* This the vtable that supports the character driver interface */

static const struct file_operations g_bmp280pressfops =
{
  bmp280_open_press,            /* open */
  bmp280_close_press,           /* close */
  bmp280_read_press,            /* read */
  bmp280_write,                 /* write */
  NULL,                         /* seek */
  bmp280_ioctl_press            /* ioctl */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  , NULL                        /* unlink */
#endif
};

static const struct file_operations g_bmp280tempfops =
{
  bmp280_open_temp,             /* open */
  bmp280_close_temp,            /* close */
  bmp280_read_temp,             /* read */
  bmp280_write,                 /* write */
  NULL,                         /* seek */
  bmp280_ioctl_temp             /* ioctl */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  , NULL                        /* unlink */
#endif
};

/* SCU instructions for pick pressure sensing data. */

static const uint16_t g_bmp280pressinst[] =
{
  SCU_INST_SEND(BMP280_PRESS_MSB),
  SCU_INST_RECV(BMP280PRESS_BYTESPERSAMPLE) | SCU_INST_LAST,
};

/* SCU instructions for pick temperature sensing data. */

static const uint16_t g_bmp280tempinst[] =
{
  SCU_INST_SEND(BMP280_TEMP_MSB),
  SCU_INST_RECV(BMP280TEMP_BYTESPERSAMPLE) | SCU_INST_LAST,
};

/* driver reference counter */

static int g_refcnt_press = 0;
static int g_refcnt_temp = 0;

/* senser calibration parameters */

static struct bmp280_press_adj_s  g_press_adj;
static struct bmp280_temp_adj_s   g_temp_adj;

/* Sequencer instance */

static struct seq_s *g_seq_press = NULL;
static struct seq_s *g_seq_temp = NULL;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: bmp280_getreg8
 *
 * Description:
 *   Read from an 8-bit BMP280 register
 *
 ****************************************************************************/

static uint8_t bmp280_getreg8(struct bmp280_dev_s *priv, uint8_t regaddr)
{
  uint8_t regval = 0;
  uint16_t inst[2];

  /* Send register to read and get the next byte */

  inst[0] = SCU_INST_SEND(regaddr);
  inst[1] = SCU_INST_RECV(1) | SCU_INST_LAST;

  scu_i2ctransfer(priv->port, priv->addr, inst, 2, &regval, 1);

  return regval;
}

/****************************************************************************
 * Name: bmp280_putreg8
 *
 * Description:
 *   Write to an 8-bit BMP280 register
 *
 ****************************************************************************/

static void bmp280_putreg8(struct bmp280_dev_s *priv, uint8_t regaddr,
                           uint8_t regval)
{
  uint16_t inst[2];

  /* Send register address and set the value */

  inst[0] = SCU_INST_SEND(regaddr);
  inst[1] = SCU_INST_SEND(regval) | SCU_INST_LAST;

  scu_i2ctransfer(priv->port, priv->addr, inst, 2, NULL, 0);
}

/****************************************************************************
 * Name: bmp280_checkid
 *
 * Description:
 *   Read and verify the BMP280 chip ID
 *
 ****************************************************************************/

static int bmp280_checkid(struct bmp280_dev_s *priv)
{
  uint8_t devid = 0;

  /* Read device ID */

  devid = bmp280_getreg8(priv, BMP280_DEVID);
  sninfo("devid: 0x%02x\n", devid);

  if (devid != (uint16_t)DEVID)
    {
      /* ID is not Correct */

      snerr("Wrong Device ID! %02x\n", devid);
      return -ENODEV;
    }

  return OK;
}

/****************************************************************************
 * Name: bmp280_get_calib_param
 *
 * Description:
 *   Read Calibration Coefficient Data
 *
 ****************************************************************************/

static int bmp280_get_calib_param_press(struct bmp280_dev_s *priv)
{
  /* Read calibration values */

  g_press_adj.dig_p1 =
        ((uint16_t)bmp280_getreg8(priv, BMP280_DIG_P1_MSB) << 8) |
         bmp280_getreg8(priv, BMP280_DIG_P1_LSB);
  g_press_adj.dig_p2 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P2_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P2_LSB);
  g_press_adj.dig_p3 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P3_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P3_LSB);
  g_press_adj.dig_p4 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P4_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P4_LSB);
  g_press_adj.dig_p5 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P5_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P5_LSB);
  g_press_adj.dig_p6 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P6_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P6_LSB);
  g_press_adj.dig_p7 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P7_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P7_LSB);
  g_press_adj.dig_p8 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P8_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P8_LSB);
  g_press_adj.dig_p9 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_P9_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_P9_LSB);

  return OK;
}

/****************************************************************************
 * Name: bmp280_get_calib_param_temp
 *
 * Description:
 *   Read Calibration Coefficient Data
 *
 ****************************************************************************/

static int bmp280_get_calib_param_temp(struct bmp280_dev_s *priv)
{
  /* Read calibration values */

  g_temp_adj.dig_t1 =
        ((uint16_t)bmp280_getreg8(priv, BMP280_DIG_T1_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_T1_LSB);
  g_temp_adj.dig_t2 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_T2_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_T2_LSB);
  g_temp_adj.dig_t3 =
        ((int16_t)bmp280_getreg8(priv, BMP280_DIG_T3_MSB) << 8) |
        bmp280_getreg8(priv, BMP280_DIG_T3_LSB);

  return OK;
}

/****************************************************************************
 * Name: bmp280_set_power_mode
 *
 * Description:
 *   set power mode
 *
 ****************************************************************************/

static void bmp280_set_power_mode(struct bmp280_dev_s *priv,
                                  uint8_t value)
{
  uint8_t v_data_u8 = 0;

  v_data_u8 = bmp280_getreg8(priv, BMP280_CTRL_MEAS);
  v_data_u8 = ((v_data_u8 & ~0x03) | value);
  bmp280_putreg8(priv, BMP280_CTRL_MEAS, v_data_u8);
}

/****************************************************************************
 * Name: bmp280_set_oversamp_press
 *
 * Description:
 *   set oversampling rate
 *
 ****************************************************************************/

static void bmp280_set_oversamp_press(struct bmp280_dev_s *priv,
                                      uint8_t value)
{
  uint8_t v_data_u8 = 0;

  v_data_u8 = bmp280_getreg8(priv, BMP280_CTRL_MEAS);
  v_data_u8 = ((v_data_u8 & ~(0x07 << 2)) | ((value << 2) & (0x07 << 2)));
  bmp280_putreg8(priv, BMP280_CTRL_MEAS, v_data_u8);
}

/****************************************************************************
 * Name: bmp280_set_oversamp_temp
 *
 * Description:
 *   set oversampling rate
 *
 ****************************************************************************/

static void bmp280_set_oversamp_temp(struct bmp280_dev_s *priv,
                                     uint8_t value)
{
  uint8_t v_data_u8 = 0;

  v_data_u8 = bmp280_getreg8(priv, BMP280_CTRL_MEAS);
  v_data_u8 = ((v_data_u8 & ~(0x07 << 5)) | ((value << 5) & (0x07 << 5)));
  bmp280_putreg8(priv, BMP280_CTRL_MEAS, v_data_u8);
}

/****************************************************************************
 * Name: bmp280_set_standby
 *
 * Description:
 *   set standby duration
 *
 ****************************************************************************/

static int bmp280_set_standby(struct bmp280_dev_s *priv, uint8_t value)
{
  uint8_t v_data_u8;
  uint8_t v_sb_u8;

  /* Set the standby duration value */

  v_data_u8 = bmp280_getreg8(priv, BMP280_CONFIG);
  v_data_u8 = (v_data_u8 & ~(0x07 << 5)) | (value << 5);
  bmp280_putreg8(priv, BMP280_CONFIG, v_data_u8);

  /* Check the standby duration value */

  v_data_u8 = bmp280_getreg8(priv, BMP280_CONFIG);
  v_sb_u8 = (v_data_u8 >> 5) & 0x07;

  if (v_sb_u8 != value)
    {
      snerr("Failed to set value for standby time.");
      return ERROR;
    }

  return OK;
}

/****************************************************************************
 * Name: bmp280_initialize
 *
 * Description:
 *   Initialize BMP280 pressure sensor
 *
 ****************************************************************************/

static int bmp280_initialize(struct bmp280_dev_s *priv)
{
  int ret;

  ret = bmp280_set_standby(priv, BMP280_STANDBY_05_MS);

  if (ret != OK)
    {
      snerr("Failed to set value for standby time.");
      return ERROR;
    }

  return OK;
}

static int bmp280_seqinit_press(struct bmp280_dev_s *priv)
{
  DEBUGASSERT(!g_seq_press);

  /* Open sequencer */

  g_seq_press = seq_open(PRESS_SEQ_TYPE, SCU_BUS_I2C0);
  if (!g_seq_press)
    {
      return -ENOENT;
    }

  priv->seq = g_seq_press;

  seq_setaddress(priv->seq, priv->addr);

  /* Set instruction and sample data information to sequencer */

  seq_setinstruction(priv->seq, g_bmp280pressinst,
                     itemsof(g_bmp280pressinst));
  seq_setsample(priv->seq, BMP280PRESS_BYTESPERSAMPLE, 0,
                BMP280PRESS_ELEMENTSIZE, false);

  return OK;
}

static int bmp280_seqinit_temp(struct bmp280_dev_s *priv)
{
  DEBUGASSERT(!g_seq_temp);

  /* Open sequencer */

  g_seq_temp = seq_open(TEMP_SEQ_TYPE, SCU_BUS_I2C0);
  if (!g_seq_temp)
    {
      return -ENOENT;
    }

  priv->seq = g_seq_temp;

  seq_setaddress(priv->seq, priv->addr);

  /* Set instruction and sample data information to sequencer */

  seq_setinstruction(priv->seq, g_bmp280tempinst,
                     itemsof(g_bmp280tempinst));
  seq_setsample(priv->seq, BMP280TEMP_BYTESPERSAMPLE, 0,
                BMP280TEMP_ELEMENTSIZE, false);

  return OK;
}

/****************************************************************************
 * Name: bmp280_open_press
 *
 * Description:
 *   This function is called whenever the BMP280 device is opened.
 *
 ****************************************************************************/

static int bmp280_open_press(struct file *filep)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;
  int ret;

  /* first pressure device initialize */

  if (g_refcnt_press == 0)
    {
      /* Open and set sequencer */

      ret = bmp280_seqinit_press(priv);
      if (ret)
        {
          return ret;
        }

      bmp280_get_calib_param_press(priv);
      bmp280_set_oversamp_press(priv, BMP280_OVERSAMP_4X);

      if (g_refcnt_temp == 0)
        {
          bmp280_set_power_mode(priv, BMP280_NORMAL_MODE);
          bmp280_initialize(priv);
        }
    }
  else
    {
      /* Set existing sequencer */

      priv->seq = g_seq_press;
    }

  g_refcnt_press++;

  return OK;
}

/****************************************************************************
 * Name: bmp280_open_temp
 *
 * Description:
 *   This function is called whenever the BMP280 device is opened.
 *
 ****************************************************************************/

static int bmp280_open_temp(struct file *filep)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;
  int ret;

  /* first temperature device initialize */

  if (g_refcnt_temp == 0)
    {
      /* Open and set sequencer */

      ret = bmp280_seqinit_temp(priv);
      if (ret)
        {
          return ret;
        }

      bmp280_get_calib_param_temp(priv);
      bmp280_set_oversamp_temp(priv, BMP280_OVERSAMP_1X);

      if (g_refcnt_press == 0)
        {
          bmp280_set_power_mode(priv, BMP280_NORMAL_MODE);
          bmp280_initialize(priv);
        }
    }
  else
    {
      /* Set existing sequencer */

      priv->seq = g_seq_temp;
    }

  g_refcnt_temp++;

  return OK;
}

/****************************************************************************
 * Name: bmp280_close_press
 *
 * Description:
 *   This routine is called when the BMP280 device is closed.
 *
 ****************************************************************************/

static int bmp280_close_press(struct file *filep)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;

  if (g_refcnt_press <= 1)
    {
      bmp280_set_oversamp_press(priv, BMP280_OVERSAMP_SKIPPED);

      /* if close last BMP280 device, enter sleep mode */

      if (g_refcnt_temp == 0)
        {
          bmp280_set_power_mode(priv, BMP280_SLEEP_MODE);
        }

      seq_close(g_seq_press);
      g_seq_press = NULL;
    }
  else
    {
      seq_ioctl(priv->seq, priv->id, SCUIOC_FREEFIFO, 0);
    }

  g_refcnt_press--;

  return OK;
}

/****************************************************************************
 * Name: bmp280_close_temp
 *
 * Description:
 *   This routine is called when the BMP280 device is closed.
 *
 ****************************************************************************/

static int bmp280_close_temp(struct file *filep)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;

  if (g_refcnt_temp <= 1)
    {
      bmp280_set_oversamp_temp(priv, BMP280_OVERSAMP_SKIPPED);

      /* if close last BMP280 device, enter sleep mode */

      if (g_refcnt_press == 0)
        {
          bmp280_set_power_mode(priv, BMP280_SLEEP_MODE);
        }

      seq_close(g_seq_temp);
      g_seq_temp = NULL;
    }
  else
    {
      seq_ioctl(priv->seq, priv->id, SCUIOC_FREEFIFO, 0);
    }

  g_refcnt_temp--;

  return OK;
}

/****************************************************************************
 * Name: bmp280_read_press
 ****************************************************************************/

static ssize_t bmp280_read_press(struct file *filep, char *buffer,
                                 size_t buflen)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;

  buflen = buflen / BMP280PRESS_BYTESPERSAMPLE * BMP280PRESS_BYTESPERSAMPLE;
  buflen = seq_read(priv->seq, priv->id, buffer, buflen);

  return buflen;
}

/****************************************************************************
 * Name: bmp280_read_temp
 ****************************************************************************/

static ssize_t bmp280_read_temp(struct file *filep, char *buffer,
                                size_t buflen)
{
  struct inode        *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;

  buflen = buflen / BMP280TEMP_BYTESPERSAMPLE * BMP280TEMP_BYTESPERSAMPLE;
  buflen = seq_read(priv->seq, priv->id, buffer, buflen);

  return buflen;
}

/****************************************************************************
 * Name: bm p280_write
 ****************************************************************************/

static ssize_t bmp280_write(struct file *filep, const char *buffer,
                            size_t buflen)
{
  return -ENOSYS;
}

/****************************************************************************
 * Name: bmp280_ioctrl_press
 ****************************************************************************/

static int bmp280_ioctl_press(struct file *filep, int cmd,
                              unsigned long arg)
{
  struct inode *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;
  int ret = OK;

  switch (cmd)
    {
      /* Get sensitivity adjustment value
       * Arg: Pointer to struct bmp280_press_adj_s
       */

      case SNIOC_GETADJ:
        {
          struct bmp280_press_adj_s *user = (struct bmp280_press_adj_s *)
                                            (uintptr_t)arg;

          user->dig_p1 = g_press_adj.dig_p1;
          user->dig_p2 = g_press_adj.dig_p2;
          user->dig_p3 = g_press_adj.dig_p3;
          user->dig_p4 = g_press_adj.dig_p4;
          user->dig_p5 = g_press_adj.dig_p5;
          user->dig_p6 = g_press_adj.dig_p6;
          user->dig_p7 = g_press_adj.dig_p7;
          user->dig_p8 = g_press_adj.dig_p8;
          user->dig_p9 = g_press_adj.dig_p9;
        }
        break;

      case SNIOC_SETSTB:
        {
          ret = bmp280_set_standby(priv, arg);
        }
        break;

      default:
        {
          if (_SCUIOCVALID(cmd))
            {
              /* Redirect SCU commands */

              ret = seq_ioctl(priv->seq, priv->id, cmd, arg);
            }
          else
            {
              snerr("Unrecognized cmd: %d\n", cmd);
              ret = - ENOTTY;
            }
        }
        break;
    }

  return ret;
}

/****************************************************************************
 * Name: bmp280_ioctrl_temp
 ****************************************************************************/

static int bmp280_ioctl_temp(struct file *filep, int cmd,
                             unsigned long arg)
{
  struct inode *inode = filep->f_inode;
  struct bmp280_dev_s *priv  = inode->i_private;
  int ret = OK;

  switch (cmd)
    {
      /* Get sensitivity adjustment value
       * Arg: Pointer to struct bmp280_temp_adj_s
       */

      case SNIOC_GETADJ:
        {
          struct bmp280_temp_adj_s *user = (struct bmp280_temp_adj_s *)
                                           (uintptr_t)arg;

          user->dig_t1 = g_temp_adj.dig_t1;
          user->dig_t2 = g_temp_adj.dig_t2;
          user->dig_t3 = g_temp_adj.dig_t3;
        }
        break;

      default:
        {
          if (_SCUIOCVALID(cmd))
            {
              /* Redirect SCU commands */

              ret = seq_ioctl(priv->seq, priv->id, cmd, arg);
            }
          else
            {
              snerr("Unrecognized cmd: %d\n", cmd);
              ret = - ENOTTY;
            }
        }
        break;
    }

  return ret;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: bmp280_init
 *
 * Description:
 *   Initialize the I2C interface to use to communicate with BMP280
 *
 * Input Parameters:
 *   i2c     - An instance of the I2C interface to use to communicate with
 *             BMP280
 *   port    - I2C port number
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno value on failure.
 *
 ****************************************************************************/

int bmp280_init(struct i2c_master_s *i2c, int port)
{
  struct bmp280_dev_s tmp;
  struct bmp280_dev_s *priv = &tmp;
  int ret;

  /* Setup temporary device structure for initialization */

  priv->i2c = i2c;
  priv->addr = BMP280_ADDR;
  priv->freq = BMP280_FREQ;
  priv->port = port;

  /* Check Device ID */

  ret = bmp280_checkid(priv);
  if (ret < 0)
    {
      snerr("Failed to register driver: %d\n", ret);
      return ERROR;
    }

  return OK;
}

/****************************************************************************
 * Name: bmp280press_register
 *
 * Description:
 *   Register the BMP280 pressure sensor character device as 'devpath'
 *
 * Input Parameters:
 *   devpath - The base path to the driver to register. E.g., "/dev/press0"
 *   dev     - An instance of the I2C interface to use to communicate with
 *             BMP280
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno value on failure.
 *
 ****************************************************************************/

int bmp280press_register(const char *devpath, int minor,
                         struct i2c_master_s *i2c, int port)
{
  struct bmp280_dev_s *priv;
  char path[12];
  int ret;

  priv = (struct bmp280_dev_s *)kmm_malloc(sizeof(struct bmp280_dev_s));
  if (!priv)
    {
      snerr("Failed to allocate instance\n");
      return -ENOMEM;
    }

  priv->i2c = i2c;
  priv->seq = NULL;
  priv->id = minor;
  priv->addr = BMP280_ADDR;
  priv->freq = BMP280_FREQ;
  priv->port = port;

  /* Register the character driver */

  snprintf(path, sizeof(path), "%s%d", devpath, minor);
  ret = register_driver(path, &g_bmp280pressfops, 0666, priv);
  if (ret < 0)
    {
      snerr("Failed to register driver: %d\n", ret);
      kmm_free(priv);
    }

  sninfo("BMP280 pressure driver loaded successfully!\n");
  return ret;
}

/****************************************************************************
 * Name: bmp280temp_register
 *
 * Description:
 *   Register the BMP280 temperature sensor character device as 'devpath'
 *
 * Input Parameters:
 *   devpath - The base path to the driver to register. E.g., "/dev/temp"
 *   dev     - An instance of the I2C interface to use to communicate with
 *             BMP280
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno value on failure.
 *
 ****************************************************************************/

int bmp280temp_register(const char *devpath, int minor,
                        struct i2c_master_s *i2c, int port)
{
  struct bmp280_dev_s *priv;
  char path[12];
  int ret;

  priv = (struct bmp280_dev_s *)kmm_malloc(sizeof(struct bmp280_dev_s));
  if (!priv)
    {
      snerr("Failed to allocate instance\n");
      return -ENOMEM;
    }

  priv->i2c = i2c;
  priv->seq = NULL;
  priv->id = minor;
  priv->addr = BMP280_ADDR;
  priv->freq = BMP280_FREQ;
  priv->port = port;

  /* Register the character driver */

  snprintf(path, sizeof(path), "%s%d", devpath, minor);
  ret = register_driver(path, &g_bmp280tempfops, 0666, priv);
  if (ret < 0)
    {
      snerr("Failed to register driver: %d\n", ret);
      kmm_free(priv);
    }

  sninfo("BMP280 temperature driver loaded successfully!\n");
  return ret;
}

#endif /* CONFIG_I2C && CONFIG_SENSORS_BMP280_SCU */
