/****************************************************************************
 * include/nuttx/sensors/adxl372.h
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 __INCLUDE_NUTTX_SENSORS_ADXL372_H
#define __INCLUDE_NUTTX_SENSORS_ADXL372_H

/****************************************************************************
 * Driver usage notes:
 *
 * This driver is a "kernel sensor leaf driver" that may be used directly
 * from user applications via the file_operations interface or have selected
 * entry points called directly from a "kernel sensor cluster driver".
 *
 * To use this driver via the file_operations interface, the board
 * initialization function should call this driver's registration function.
 * The driver will register itself with NuttX under the /dev path that is
 * provided by the config structure.  Then user applications may access the
 * driver via the "file descriptor handle" returned by the file_operations
 * open() function.
 *
 * By default the open() function configures the sensor for:
 *
 *   Output Data Rate (ODR) = 1600 Hz.
 *   Bandwidth (BW) = 800 Hz.
 *   Normal mode sampling (as opposed to low power mode sampling).
 *   The Low Pass Filter is enabled and the High Pass Filter is disabled.
 *   A filter settling time of 370ms is selected.
 *
 * If the user desires different configuration settings, then the user may
 * either provide a pointer to an array of "struct adxl372_reg_pair_s" that
 * will be applied to the sensor upon open(); or dynamically use the lseek()
 * and write() file_operations functions to set the sensor configuration as
 * desired.
 *
 * When using the sensor from the file_operations interface, the sensor is
 * accessed in Programmed I/O (PIO) mode. (i.e. When the read() function is
 * executed, the sensor is read on that thread.) PIO reads and writes block
 * the calling thread until data is available. Since the sensor is on an SPI
 * bus running at near 10 MHz, the read or write operations should only take
 * a few microseconds (about a microsecond per byte of data), so for
 * individual sensor reads and writes, the overhead of using interrupts or
 * DMA is not worthwhile.
 *
 * This driver supports the Common Sensor Register Interface.
 * See Documentation/components/drivers/special/sensors/sensors_cluster.rst
 * for details.
 *
 * It also extends the interface by permitting cluster driver calls to
 * a function that is intended to perform high performance DMA SPI exchange
 * operations. See the usage note on the exchange operation below.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/irq.h>
#include <nuttx/config.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi/spi.h>
#include <nuttx/sensors/cluster_driver.h>

#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_ADXL372) \
    && defined(CONFIG_SPI_EXCHANGE)

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

/* ADXL372 common definitions */

#define ADXL372_READ                    0x01
#define ADXL372_WRITE                   0x00
#define ADXL372_ADDR_MASK               0x0fe

/* ADXL372 Accelerometer Register definitions */

#define ADXL372_DEVID_AD                0x00
#   define ADXL372_DEVID_AD_VALUE       0xad
#define ADXL372_DEVID_MST               0x01
#   define ADXL372_DEVID_MST_VALUE      0x1d
#define ADXL372_PARTID                  0x02
#   define ADXL372_PARTID_VALUE         0xfa
#define ADXL372_REVID                   0x03
#define ADXL372_STATUS                  0x04
#define ADXL372_STATUS2                 0x05
#define ADXL372_FIFO_ENTRIES2           0x06
#define ADXL372_FIFO_ENTRIES            0x07
#define ADXL372_XDATA_H                 0x08
#define ADXL372_XDATA_L                 0x09
#define ADXL372_YDATA_H                 0x0a
#define ADXL372_YDATA_L                 0x0b
#define ADXL372_ZDATA_H                 0x0c
#define ADXL372_ZDATA_L                 0x0d
#define ADXL372_THRESH_ACT_X_H          0x23
#define ADXL372_FIFO_CTL                0x3a
#   define ADXL372_FIFO_BYPASSED        0x00
#   define ADXL372_FIFO_STREAMED        0x02
#define ADXL372_INT1_MAP                0x3b
#   define ADXL372_INT1_MAP_DR          0x01
#   define ADXL372_INT1_MAP_FRDY        0x02
#   define ADXL372_INT1_MAP_FFULL       0x04
#define ADXL372_TIMING                  0x3d
#   define ADXL372_TIMING_ODR400        (0x0 << 5)      /* 400 Hz ODR */
#   define ADXL372_TIMING_ODR800        (0x1 << 5)      /* 800 Hz ODR */
#   define ADXL372_TIMING_ODR1600       (0x2 << 5)      /* 1600 Hz ODR */
#   define ADXL372_TIMING_ODR3200       (0x3 << 5)      /* 3200 Hz ODR */
#   define ADXL372_TIMING_ODR6400       (0x4 << 5)      /* 6400 Hz ODR */
#define ADXL372_MEASURE                 0x3e
#   define ADXL372_MEAS_BW200           0x0     /* 200 Hz Bandwidth */
#   define ADXL372_MEAS_BW400           0x1     /* 400 Hz Bandwidth */
#   define ADXL372_MEAS_BW800           0x2     /* 800 Hz Bandwidth */
#   define ADXL372_MEAS_BW1600          0x3     /* 1600 Hz Bandwidth */
#   define ADXL372_MEAS_BW3200          0x4     /* 3200 Hz Bandwidth */
#define ADXL372_POWER_CTL               0x3f
#   define ADXL372_POWER_LPF_DISABLE    (1 << 3)
#   define ADXL372_POWER_HPF_DISABLE    (1 << 2)
#   define ADXL372_POWER_MODE_STANDBY   0x0
#   define ADXL372_POWER_MODE_WAKEUP    0x1
#   define ADXL372_POWER_MODE_INSTON    0x2
#   define ADXL372_POWER_MODE_MEASURE   0x3
#define ADXL372_RESET                   0x41
#   define ADXL372_RESET_VALUE          0x52
#define ADXL372_FIFO_DATA               0x42
#define ADXL372_LAST                    0x42
#define ADXL372_SCRATCH                 ADXL372_THRESH_ACT_X_H

/* SPI Bus Parameters */

#define ADXL372_SPI_FREQUENCY           (10000000)     /* 10 MHz */
#define ADXL372_SPI_MODE                (SPIDEV_MODE0) /* SPI Mode 0: CPOL=0,CPHA=0 */

/****************************************************************************
 * Public Types
 ****************************************************************************/

#ifndef CONFIG_SENSORS_ADXL372_UORB

/* A reference to a structure of this type must be passed to the ADXL372
 * driver. This structure provides information about the configuration
 * of the sensor and provides some board-specific hooks.
 *
 * This sensor driver presents two interfaces, the POSIX character driver
 * interface (fops) that is intended for use from a user application, and
 * a set of direct call entry points that are intended to be used by
 * a sensor cluster driver that is running as a kernel task (a driver to
 * driver interface).  Application tasks should not attempt to call sensor
 * cluster driver entry points.
 *
 * Memory for this structure is provided by the caller.  It is not copied
 * by the driver and is presumed to persist while the driver is active.
 */

struct adxl372_reg_pair_s  /* Utility struct for the below... */
{
  uint8_t addr;            /* SPI register address */
  uint8_t value;           /* Value to be stored in the above reg on open() */
};

struct adxl372_dvr_entry_vector_s
{
  struct sensor_cluster_operations_s c;

  /* Extend the sensor cluster driver interface with a SPI DMA exchange
   * transfer. The standard driver_read and driver_write perform PIO
   * transfers. The will loop waiting on the SPI hardware and are only
   * appropriate for short data transfers.
   * Note that the first byte in the tx buffer must be a command/address
   * byte. The exchange function does not provide one. Also note that
   * the first byte stored in the rxbuffer is a garbage byte, which
   * is natural for a SPI exchange transfer. Plan your buffer accordingly.
   */

  CODE void (*driver_spiexc)(FAR void *instance_handle,
                             FAR const void *txbuffer,
                             FAR void *rxbuffer, size_t nwords);
};

struct adxl372_config_s
{
  /* Since multiple ADXL372 can be connected to the same SPI bus we need
   * to use multiple SPI device ids which are employed by NuttX to select/
   * deselect the desired ADXL372 chip via their chip select inputs.
   */

  int spi_devid;

  /* Initial control register configuration values. */

  uint16_t initial_cr_values_size;  /* size of the below array.
                                     * 0 = use default values. */

  /* The initial value store operations will occur in the order they
   * appear in the array.
   */

  struct adxl372_reg_pair_s *initial_cr_values;

  /* The below fields are intended for the sensor cluster driver interface
   * and may be ignored when the sensor cluster driver is not being used.
   * The leaf driver's registration function fills in the below fields.
   */

  /* Leaf sensor handle for sensor cluster kernel driver */

  FAR struct spi_dev_s *leaf_handle;

  /* Pointer to the leaf driver's sensor_cluster_operations_s structure */

  FAR const struct adxl372_dvr_entry_vector_s *sc_ops;
};
#endif

/****************************************************************************
 * Public Function Prototypes
 ****************************************************************************/

#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif

/****************************************************************************
 * Name: adxl372_register
 *
 * Description:
 *   Register the ADXL372 character device as 'devpath'
 *
 * Input Parameters:
 *   devpath - The full path to the driver to register. E.g., "/dev/acl0"
 *   spi     - An instance of the SPI interface to use to communicate with
 *             ADXL372
 *   config  - configuration for the ADXL372 accelerometer driver.  For
 *             details see description above.
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno value on failure.
 *
 ****************************************************************************/

#ifndef CONFIG_SENSORS_ADXL372_UORB
int adxl372_register(FAR const char *devpath,
                     FAR struct spi_dev_s *spi,
                     FAR struct adxl372_config_s *config);
#else
int adxl372_register_uorb(int devno, FAR struct spi_dev_s *spi);
#endif

#undef EXTERN
#ifdef __cplusplus
}
#endif

#endif /* CONFIG_SPI && CONFIG_SENSORS_ADXL372 && CONFIG_SPI_EXCHANGE */
#endif /* __INCLUDE_NUTTX_SENSORS_ADXL372_H */
