nuttx/drivers/sensors/lsm330_spi.c
Alin Jerpelea 89a10f9899 NuttX: Robert A. Feretich: update licenses to Apache
Robert A. Feretich has submitted the ICLA and we can migrate the licenses
 to Apache.

RAF Research LLC  has submitted the SGA and we can migrate the licenses
 to Apache.

Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
2021-04-01 12:13:12 -05:00

1433 lines
43 KiB
C

/****************************************************************************
* drivers/sensors/lsm330_spi.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>
#if defined(CONFIG_SPI) && defined(CONFIG_SENSORS_LSM330SPI) \
&& defined(CONFIG_SPI_EXCHANGE)
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/semaphore.h>
#include <nuttx/sensors/lsm330.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define LSM330_INITIAL_ACLCR_SIZE 7
#define LSM330_INITIAL_GYROCR_SIZE 5
/****************************************************************************
* Private structure definitions
****************************************************************************/
struct sensor_data_s
{
int16_t x_gyr; /* Measurement result for x axis */
int16_t y_gyr; /* Measurement result for y axis */
int16_t z_gyr; /* Measurement result for z axis */
};
struct lsm330_dev_s
{
FAR struct lsm330_dev_s *flink; /* Supports a singly linked list of
* drivers */
FAR struct spi_dev_s *spi; /* Pointer to the SPI instance */
FAR struct lsm330_config_s *config; /* Pointer to the configuration of the
* LSM330 sensor */
sem_t devicesem; /* Manages exclusive access to this
* device */
sem_t datasem; /* Manages exclusive access to this
* structure */
struct sensor_data_s data; /* The data as measured by the sensor */
uint8_t seek_address; /* Current device address. */
uint8_t readonly; /* 0 = writing to the device in enabled */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr);
static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_write_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
uint8_t reg_data);
static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt);
static void lsm330acl_reset(FAR struct lsm330_dev_s *dev);
static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev);
static int lsm330acl_open(FAR struct file *filep);
static int lsm330gyro_open(FAR struct file *filep);
static int lsm330acl_close(FAR struct file *filep);
static int lsm330gyro_close(FAR struct file *filep);
static ssize_t lsm330acl_read(FAR struct file *, FAR char *, size_t);
static ssize_t lsm330gyro_read(FAR struct file *, FAR char *, size_t);
static ssize_t lsm330acl_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static ssize_t lsm330gyro_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static off_t lsm330acl_seek(FAR struct file *filep, off_t offset,
int whence);
static off_t lsm330gyro_seek(FAR struct file *filep, off_t offset,
int whence);
static int lsm330_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg);
static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg);
static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg);
static int lsm330gyro_dvr_close(FAR void *instance_handle,
int32_t arg);
static ssize_t lsm330acl_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen);
static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen);
static ssize_t lsm330acl_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen);
static off_t lsm330acl_dvr_seek(FAR void *instance_handle, off_t offset,
int whence);
static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset,
int whence);
static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd,
unsigned long arg);
static void lsm330_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_lsm330a_fops =
{
lsm330acl_open,
lsm330acl_close,
lsm330acl_read,
lsm330acl_write,
lsm330acl_seek,
lsm330_ioctl,
NULL
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL
#endif
};
static const struct file_operations g_lsm330g_fops =
{
lsm330gyro_open,
lsm330gyro_close,
lsm330gyro_read,
lsm330gyro_write,
lsm330gyro_seek,
lsm330_ioctl,
NULL
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL
#endif
};
static const struct lsm330spi_dvr_entry_vector_s g_lsm330acl_dops =
{
{ /* Standard sensor cluster driver entry-vector */
.driver_open = lsm330acl_dvr_open,
.driver_close = lsm330acl_dvr_close,
.driver_read = lsm330acl_dvr_read,
.driver_write = lsm330acl_dvr_write,
.driver_seek = lsm330acl_dvr_seek,
.driver_ioctl = lsm330_dvr_ioctl,
.driver_suspend = 0,
.driver_resume = 0,
},
/* lsm330 extensions follow */
.driver_spiexc = lsm330_dvr_exchange,
};
static const struct lsm330spi_dvr_entry_vector_s g_lsm330gyro_dops =
{
{ /* Standard sensor cluster driver entry-vector */
.driver_open = lsm330gyro_dvr_open,
.driver_close = lsm330gyro_dvr_close,
.driver_read = lsm330gyro_dvr_read,
.driver_write = lsm330gyro_dvr_write,
.driver_seek = lsm330gyro_dvr_seek,
.driver_ioctl = lsm330_dvr_ioctl,
.driver_suspend = 0,
.driver_resume = 0,
},
/* lsm330 extensions follow */
.driver_spiexc = lsm330_dvr_exchange,
};
/* Single linked list to store instances of drivers */
static struct lsm330_dev_s *g_lsm330a_list = NULL;
static struct lsm330_dev_s *g_lsm330g_list = NULL;
/* Default accelerometer initialization sequence:
*
* Configure LSM330 to measure mode.
*
* 1. CR5: ODR=0, power-off
* 2. CR6: Bandwidth=800Hz. +/-16g range. 4-wire SPI.
* 3. CR7: Make sure auto-increment is turned on.
* 4. CR2: Zero state machine 1.
* 5. CR3: Zero state machine 2.
* 6. CR4: DataReady interrupt disabled.
* 7. CR5: Power up. 1600Hz sample rate. X,Y, & Z enabled.
*/
static struct lsm330_reg_pair_s g_default_lsm330_aclcr_values[] =
{
/* CR5 ODR[3:0] BDU ZEN YEN XEN
* 0000=Off 0 0 0 0=all disabled
*/
{
.addr = LSM330_ACL_CTRL_REG5,
.value = 0x00
},
/* CR6 BW[2:1] FSCALE[2:0] - - SIM
* 00=800Hz 10 0=16g 0 0 0=4-wire
*/
{
.addr = LSM330_ACL_CTRL_REG6,
.value = 0x20
},
/* CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN
* 0 0 0 1 0 0 0 0
*/
{
.addr = LSM330_ACL_CTRL_REG7,
.value = 0x10
},
/* CR2 HYST1 - SM1_PIN - - SM1_EN
* 000 0 0 0 0 0
*/
{
.addr = LSM330_ACL_CTRL_REG2,
.value = 0x00
},
/* CR3 HYST2 - SM2_PIN - - SM2_EN
* 000 0 0 0 0 0
*/
{
.addr = LSM330_ACL_CTRL_REG3,
.value = 0x00
},
/* CR4 DR_EN IEA IEL INT2_EN INT1_EN VFILT STRT
* 1 1 0 0 0 0 0
*/
{
.addr = LSM330_ACL_CTRL_REG4,
.value = 0xc0
},
/* CR5 ODR[3:0] BDU ZEN YEN XEN
* 1001=1600Hz 1 1 1 1=all enabled
*/
{
.addr = LSM330_ACL_CTRL_REG5,
.value = 0x9f
}
};
/* Default gyroscope initialization sequence
*
* Configure LSM330 Gyroscope to measure mode.
*
* 1. CR1: Power up. 760Hz sample rate. Bandwidth=100Hz. X,Y, & Z enabled.
* 2. CR2: Normal measurement mode. High pass filter OFF.
* 3. CR3: All interrupts disabled.
* 4. CR4: +/-500dps range. Block update. 4-wire SPI.
* 5. CR5: Select Low Pass Filter 1 outputs. LPF2 bypassed.
*/
static struct lsm330_reg_pair_s g_default_lsm330_gyrocr_values[] =
{
/* CR1 DR[1:0] BW[1:0] PD Zen Xen Yen
* 1 1=760Hz 1 1=100Hz 1 1 1 1
*/
{
.addr = LSM330_GYRO_CTRL_REG1,
.value = 0xff
},
/* CR2 EXTRen LVLen HPM[1:0] HPCF[3:0]
* 0 0 00=Normal xxxx Default
*/
{
.addr = LSM330_GYRO_CTRL_REG2,
.value = 0x00
},
/* CR3 I1_Int1 I1_Boot H_Lactive PP_OD I2_DRDY I2_WTM I2_ORun I2_Empty
* 0 0 0 0 0 0 0 0
*/
{
.addr = LSM330_GYRO_CTRL_REG3,
.value = 0x00
},
/* CR4 BDU BLE FS[1:0] 0 0 0 SIM
* 1 0 01=500dps 0 0 0 0=4-wire
*/
{
.addr = LSM330_GYRO_CTRL_REG4,
.value = 0x90
},
/* CR5 BOOT FIFO_EN - HPen INT1_Sel[1:0] Out_Sel[1:0]
* 0 0 0 0 00=LPF1 00=LPF1
*/
{
.addr = LSM330_GYRO_CTRL_REG5,
.value = 0x0a
}
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lsm330_read_register
****************************************************************************/
static uint8_t lsm330_read_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr)
{
uint8_t reg_data;
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to read. */
SPI_SEND(dev->spi, reg_addr | LSM330_READ);
/* Write an idle byte while receiving the requested data */
reg_data = (uint8_t) (SPI_SEND(dev->spi, 0xff));
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
return reg_data;
}
/****************************************************************************
* Name: lsm330_read_acl_registerblk
****************************************************************************/
static void lsm330_read_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to start reading */
SPI_SEND(dev->spi, reg_addr | LSM330_READ);
/* Write idle bytes while receiving the requested data */
while (0 != xfercnt--)
{
*reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_read_gyro_registerblk
****************************************************************************/
static void lsm330_read_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address from where we want to start reading */
SPI_SEND(dev->spi, reg_addr | LSM330_READ |
(xfercnt > 1 ? LSM330_GYRO_AUTO : 0));
/* Write idle bytes while receiving the requested data */
while (0 != xfercnt--)
{
*reg_data++ = (uint8_t)SPI_SEND(dev->spi, 0xff);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_register
****************************************************************************/
static void lsm330_write_register(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
uint8_t reg_data)
{
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low to select the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to write */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE);
/* Transmit the content which should be written into the register */
SPI_SEND(dev->spi, reg_data);
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_acl_registerblk
****************************************************************************/
static void lsm330_write_acl_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to start writing */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE);
/* Transmit the content which should be written in the register block */
while (0 != xfercnt--)
{
SPI_SEND(dev->spi, *reg_data++);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330_write_gyro_registerblk
****************************************************************************/
static void lsm330_write_gyro_registerblk(FAR struct lsm330_dev_s *dev,
uint8_t reg_addr,
FAR uint8_t *reg_data,
uint8_t xfercnt)
{
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(dev->spi, true);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(dev->spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(dev->spi, LSM330_SPI_MODE);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, true);
/* Transmit the register address to where we want to start writing */
SPI_SEND(dev->spi, reg_addr | LSM330_WRITE |
(xfercnt > 1 ? LSM330_GYRO_AUTO : 0));
/* Transmit the content which should be written in the register block */
while (0 != xfercnt--)
{
SPI_SEND(dev->spi, *reg_data++);
}
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(dev->spi, dev->config->spi_devid, false);
/* Unlock the SPI bus */
SPI_LOCK(dev->spi, false);
}
/****************************************************************************
* Name: lsm330acl_reset
****************************************************************************/
static void lsm330acl_reset(FAR struct lsm330_dev_s *dev)
{
/* Reset LSM330 Accelerometer. Write only. Begin a boot.
* Note that the LSM330 ACL does not set the BOOT bit for read, so we
* can't loop on it.
*/
lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, LSM330_ACR7_BOOT);
/* Wait for boot to finish */
up_mdelay(20);
/* Set auto-increment.
*
* CR7 BOOT FIFO_EN WTM_EN ADD_INC P1_MTY P1_WTM P1_OVR WTM_EN
* 0 0 0 1 0 0 0 0
*/
lsm330_write_register(dev, LSM330_ACL_CTRL_REG7, 0x10);
}
/****************************************************************************
* Name: lsm330gyro_reset
****************************************************************************/
static void lsm330gyro_reset(FAR struct lsm330_dev_s *dev)
{
/* Reset LSM330 Gyroscope. Write only. Begin a boot.
* Note that the LSM330 ACL does not set the BOOT bit for read, so we
* can't loop on it.
*/
lsm330_write_register(dev, LSM330_GYRO_CTRL_REG5, LSM_GYRO_BOOT_MASK);
/* Wait for boot to finish */
up_mdelay(20);
}
/****************************************************************************
* Name: lsm330acl_dvr_open
****************************************************************************/
static int lsm330acl_dvr_open(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct lsm330_reg_pair_s *initp;
uint8_t reg_content;
int ret;
int sz;
int i;
sninfo("lsm330acl_open: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
ret = nxsem_trywait(&priv->devicesem);
if (ret < 0)
{
sninfo("INFO: LSM330 Accelerometer is already open.\n");
return -EBUSY;
}
/* Read the ID Register */
priv->readonly = false;
reg_content = lsm330_read_register(priv, LSM330_ACL_IDREG);
sninfo("LSM330_ACL_IDREG = 0x%02x\n", reg_content);
if (reg_content != LSM330_ACL_IDREG_VALUE)
{
/* Made info log level to permit open being used as a device probe. */
snwarn("INFO: Device ID (0x%02X) "
"does not match expected LSM330 Acl ID (0x%02).\n",
reg_content, LSM330_ACL_IDREG_VALUE);
priv->readonly = true;
}
else /* ID matches */
{
lsm330acl_reset(priv); /* Perform a sensor reset */
/* Choose the initialization sequence */
if (priv->config->initial_cr_values_size == 0 ||
priv->config->initial_cr_values == NULL)
{
initp = g_default_lsm330_aclcr_values; /* Default values */
sz = LSM330_INITIAL_ACLCR_SIZE;
sninfo("Using default CRs\n");
}
else
{
initp = priv->config->initial_cr_values; /* User supplied values */
sz = priv->config->initial_cr_values_size;
sninfo("Using provided CRs\n");
}
/* Apply the initialization sequence */
for (i = 0; i < sz; i++)
{
lsm330_write_register(priv, initp[i].addr, initp[i].value);
}
#ifdef CONFIG_DEBUG_SENSORS_INFO
/* Read back the content of all control registers for debug purposes */
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG5);
sninfo("LSM330_ACL_CTRL_REG5 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG7);
sninfo("LSM330_ACL_CTRL_REG7 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG6);
sninfo("LSM330_ACL_CTRL_REG6 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_ACL_CTRL_REG4);
sninfo("LSM330_ACL_CTRL_REG4 = 0x%02x\n", reg_content);
#endif
}
priv->seek_address = (uint8_t) LSM330_ACL_OUT_X_L;
return OK;
}
/****************************************************************************
* Name: lsm330gyro_dvr_open
****************************************************************************/
static int lsm330gyro_dvr_open(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct lsm330_reg_pair_s *initp;
uint8_t reg_content;
int ret;
int sz;
int i;
sninfo("lsm330gyro_open: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
ret = nxsem_trywait(&priv->devicesem);
if (ret < 0)
{
sninfo("INFO: LSM330 Gyroscope is already open.\n");
return -EBUSY;
}
/* Read the ID Register */
priv->readonly = false;
reg_content = lsm330_read_register(priv, LSM330_GYRO_IDREG);
sninfo("LSM330_GYRO_IDREG = 0x%02x\n", reg_content);
if (reg_content != LSM330_GYRO_IDREG_VALUE)
{
/* Made warning log level to permit open being used as
* a device probe.
*/
snwarn("INFO: Device ID (0x%02X) "
"does not match expected LSM330 Gyro ID (0x%02).\n",
reg_content, LSM330_GYRO_IDREG_VALUE);
priv->readonly = true;
}
else /* ID matches */
{
lsm330gyro_reset(priv); /* Perform a sensor reset */
/* Choose the initialization sequence */
if (priv->config->initial_cr_values_size == 0 ||
priv->config->initial_cr_values == NULL)
{
initp = g_default_lsm330_gyrocr_values; /* Default values */
sz = LSM330_INITIAL_GYROCR_SIZE;
}
else
{
initp = priv->config->initial_cr_values; /* User supplied values */
sz = priv->config->initial_cr_values_size;
}
/* Apply the initialization sequence */
for (i = 0; i < sz; i++)
{
lsm330_write_register(priv, initp[i].addr, initp[i].value);
}
#ifdef CONFIG_DEBUG_SENSORS_INFO
/* Read back the content of all control registers for debug purposes */
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG1);
sninfo("LSM330_GYRO_CTRL_REG1 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG2);
sninfo("LSM330_GYRO_CTRL_REG2 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG3);
sninfo("LSM330_GYRO_CTRL_REG3 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG4);
sninfo("LSM330_GYRO_CTRL_REG4 = 0x%02x\n", reg_content);
reg_content = lsm330_read_register(priv, LSM330_GYRO_CTRL_REG5);
sninfo("LSM330_GYRO_CTRL_REG5 = 0x%02x\n", reg_content);
#endif
}
priv->seek_address = (uint8_t) LSM330_GYRO_OUT_X_L;
return OK;
}
/****************************************************************************
* Name: lsm330acl_dvr_close
****************************************************************************/
static int lsm330acl_dvr_close(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
UNUSED(arg);
/* Perform a reset to put the sensor in standby mode. */
lsm330acl_reset(priv);
/* Release the sensor */
nxsem_post(&priv->devicesem);
return OK;
}
/****************************************************************************
* Name: lsm330gyro_dvr_close
****************************************************************************/
static int lsm330gyro_dvr_close(FAR void *instance_handle, int32_t arg)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
sninfo("lsm330gyro_close: entered...\n");
DEBUGASSERT(priv != NULL);
UNUSED(arg);
/* Perform a reset to put the sensor in standby mode. */
lsm330gyro_reset(priv);
/* Release the sensor */
nxsem_post(&priv->devicesem);
return OK;
}
/****************************************************************************
* Name: lsm330acl_dvr_read
****************************************************************************/
static ssize_t lsm330acl_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
lsm330_read_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330gyro_dvr_read
****************************************************************************/
static ssize_t lsm330gyro_dvr_read(FAR void *instance_handle,
FAR char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
lsm330_read_gyro_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330acl_dvr_write
****************************************************************************/
static ssize_t lsm330acl_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
if (priv->readonly)
{
return -EROFS;
}
lsm330_write_acl_registerblk(priv, priv->seek_address, (uint8_t *)buffer,
buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330gyro_dvr_write
****************************************************************************/
static ssize_t lsm330gyro_dvr_write(FAR void *instance_handle,
FAR const char *buffer, size_t buflen)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
DEBUGASSERT(priv != NULL);
if (priv->readonly)
{
return -EROFS;
}
lsm330_write_gyro_registerblk(priv, priv->seek_address,
(FAR uint8_t *)buffer, buflen);
return buflen;
}
/****************************************************************************
* Name: lsm330acl_dvr_seek
****************************************************************************/
static off_t lsm330acl_dvr_seek(FAR void *instance_handle,
off_t offset, int whence)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
off_t reg;
DEBUGASSERT(priv != NULL);
switch (whence)
{
case SEEK_CUR: /* incremental seek */
reg = priv->seek_address + offset;
if (0 > reg || reg > LSM330_ACL_LAST)
{
return -EINVAL;
}
priv->seek_address = reg;
break;
case SEEK_END: /* seek to the 1st X-data register */
priv->seek_address = LSM330_ACL_OUT_X_L;
break;
case SEEK_SET: /* seek to designated address */
if (0 > offset || offset > LSM330_ACL_LAST)
{
return -EINVAL;
}
priv->seek_address = offset;
break;
default: /* Invalid whence */
return -EINVAL;
}
return priv->seek_address;
}
/****************************************************************************
* Name: lsm330gyro_dvr_seek
****************************************************************************/
static off_t lsm330gyro_dvr_seek(FAR void *instance_handle, off_t offset,
int whence)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
off_t reg;
DEBUGASSERT(priv != NULL);
switch (whence)
{
case SEEK_CUR: /* incremental seek */
reg = priv->seek_address + offset;
if (0 > reg || reg > LSM330_GYRO_LAST)
{
return -EINVAL;
}
priv->seek_address = reg;
break;
case SEEK_END: /* seek to the 1st data register */
priv->seek_address = LSM330_GYRO_OUT_X_L;
break;
case SEEK_SET: /* seek to designated address */
if (0 > offset || offset > LSM330_GYRO_LAST)
{
return -EINVAL;
}
priv->seek_address = offset;
break;
default: /* Invalid whence */
return -EINVAL;
}
return priv->seek_address;
}
/****************************************************************************
* Name: lsm330_dvr_ioctl
****************************************************************************/
static int lsm330_dvr_ioctl(FAR void *instance_handle, int cmd,
unsigned long arg)
{
int ret = OK;
switch (cmd)
{
/* Command was not recognized */
default:
snerr("ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}
/****************************************************************************
* Name: lsm330_dvr_exchange (with SPI DMA capability)
*
* Description:
* Exchange a block of data on SPI using DMA
*
* Input Parameters:
* instance_handle - Pointer to struct lsm330_dev_s.
* txbuffer - A pointer to the buffer of data to be sent
* rxbuffer - A pointer to a buffer in which to receive data
* nwords - the length of data to be exchanged in units of words.
* The wordsize is determined by the number of bits-per-word
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits >8, the data is packed into
* uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
static void lsm330_dvr_exchange(FAR void *instance_handle,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
{
FAR struct lsm330_dev_s *priv = (FAR struct lsm330_dev_s *)instance_handle;
FAR struct spi_dev_s *spi = priv->spi;
sninfo("In lsm330_dvr_exchange: Handle=0x%08X\n", instance_handle);
/* Lock the SPI bus so that only one device can access it
* at the same time
*/
SPI_LOCK(spi, true);
SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(spi, LSM330_SPI_MODE);
sninfo("Calling SPI_EXCHANGE: devid=0x%08X\n", priv->config->spi_devid);
/* Set CS to low which selects the LSM330 */
SPI_SELECT(spi, priv->config->spi_devid, true);
/* Perform an SPI exchange block operation. */
SPI_EXCHANGE(spi, txbuffer, rxbuffer, nwords);
/* Set CS to high to deselect the LSM330 */
SPI_SELECT(spi, priv->config->spi_devid, false);
sninfo("Returned from : SPI_EXCHANGE\n");
/* Unlock the SPI bus */
SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: lsm330acl_open
****************************************************************************/
static int lsm330acl_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330acl_dvr_open((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330gyro_open
****************************************************************************/
static int lsm330gyro_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330gyro_dvr_open((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330acl_close
****************************************************************************/
static int lsm330acl_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330acl_dvr_close((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330gyro_close
****************************************************************************/
static int lsm330gyro_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
int ret;
ret = lsm330gyro_dvr_close((FAR void *)priv, 0);
return ret;
}
/****************************************************************************
* Name: lsm330acl_read
****************************************************************************/
static ssize_t lsm330acl_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_read(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330gyro_read
****************************************************************************/
static ssize_t lsm330gyro_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_read(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330acl_write
****************************************************************************/
static ssize_t lsm330acl_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_write(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330gyro_write
****************************************************************************/
static ssize_t lsm330gyro_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_write(priv, buffer, buflen);
}
/****************************************************************************
* Name: lsm330acl_seek
****************************************************************************/
static off_t lsm330acl_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330acl_dvr_seek(priv, offset, whence);
}
/****************************************************************************
* Name: lsm330gyro_seek
****************************************************************************/
static off_t lsm330gyro_seek(FAR struct file *filep,
off_t offset, int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330gyro_dvr_seek(priv, offset, whence);
}
/****************************************************************************
* Name: lsm330_ioctl
****************************************************************************/
static int lsm330_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lsm330_dev_s *priv = inode->i_private;
return lsm330_dvr_ioctl(priv, cmd, arg);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lsm330_register
*
* Description:
* Register the LSM330 character device as 'devpath'
*
* Input Parameters:
* devpath_acl - The full path to the driver to register. E.g.,
* "/dev/acl0"
* devpath_gyro - The full path to the driver to register. E.g.,
* "/dev/gyr0"
* spi - An instance of the SPI interface to use to communicate
* with LSM330
* config_acl - configuration for the LSM330 accelerometer driver.
* For details see description above.
* config_gyro - configuration for the LSM330 gyroscope driver.
* For details see description above.
* caller_is_driver - 0 (false) Driver user is a user application using
* the fops interface.
* 1 (true) "Driver to Driver" interface will be used
* in addition to the fops interface.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int lsm330_register(FAR const char *devpath_acl,
FAR const char *devpath_gyro,
FAR struct spi_dev_s *spi,
FAR struct lsm330_config_s *config_acl,
FAR struct lsm330_config_s *config_gyro)
{
FAR struct lsm330_dev_s *priva;
FAR struct lsm330_dev_s *priv;
int ret;
/* Sanity check */
DEBUGASSERT(spi != NULL);
DEBUGASSERT(config_acl != NULL);
DEBUGASSERT(config_gyro != NULL);
config_acl->leaf_handle = NULL;
config_gyro->leaf_handle = NULL;
config_acl->sc_ops = NULL;
config_gyro->sc_ops = NULL;
/* Initialize the LSM330 accelerometer device structure. */
priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s));
if (priv == NULL)
{
snerr("ERROR: Failed to allocate accelerometer instance\n");
return -ENOMEM;
}
priv->spi = spi;
priv->config = config_acl;
/* Initialize sensor and sensor data access semaphore */
nxsem_init(&priv->devicesem, 0, 1);
nxsem_init(&priv->datasem, 0, 1);
/* Setup SPI frequency and mode */
SPI_SETFREQUENCY(spi, LSM330_SPI_FREQUENCY);
SPI_SETMODE(spi, LSM330_SPI_MODE);
/* Register the character driver */
ret = register_driver(devpath_acl, &g_lsm330a_fops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: Failed to register accelerometer driver: %d\n", ret);
nxsem_destroy(&priv->datasem);
kmm_free(priv);
return ret;
}
/* Since we support multiple LSM330 devices, we will need to add this new
* instance to a list of device instances so that it can be found by the
* interrupt handler based on the received IRQ number.
*/
priv->flink = g_lsm330a_list;
g_lsm330a_list = priv;
priva = priv;
config_acl->leaf_handle = (void *) priv;
/* Initialize the LSM330 gyroscope device structure. */
priv = (FAR struct lsm330_dev_s *)kmm_malloc(sizeof(struct lsm330_dev_s));
if (priv == NULL)
{
snerr("ERROR: Failed to allocate gyroscope instance\n");
ret = -ENOMEM;
goto err_exit;
}
priv->spi = spi;
priv->config = config_gyro;
/* Initialize sensor and sensor data access semaphore */
nxsem_init(&priv->devicesem, 0, 1);
nxsem_init(&priv->datasem, 0, 1);
/* Register the character driver */
ret = register_driver(devpath_gyro, &g_lsm330g_fops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: Failed to register gyroscope driver: %d\n", ret);
nxsem_destroy(&priv->datasem);
kmm_free(priv);
goto err_exit;
}
/* Since we support multiple LSM330 devices, we will need to add this new
* instance to a list of device instances so that it can be found by the
* interrupt handler based on the received IRQ number.
*/
priv->flink = g_lsm330g_list;
g_lsm330g_list = priv;
config_gyro->leaf_handle = (void *) priv;
config_acl->sc_ops = &g_lsm330acl_dops;
config_gyro->sc_ops = &g_lsm330gyro_dops;
/* If this is part of a kernel controlled sensor cluster driver,
* then return a handle to the caller
*/
return OK;
err_exit:
/* Registration the of the gyroscope failed, so we need to destroy the
* accelerometer instance.
*/
nxsem_destroy(&priva->datasem);
kmm_free(priva);
return ret;
}
#endif /* CONFIG_SPI && CONFIG_SENSORS_LSM330SPI && CONFIG_SPI_EXCHANGE */