261 lines
6.3 KiB
C
261 lines
6.3 KiB
C
/* ST Microelectronics LPS28DFW pressure and temperature sensor
|
|
*
|
|
* Copyright (c) 2023 STMicroelectronics
|
|
* Copyright (c) 2023 PHYTEC Messtechnik GmbH
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "lps2xdf.h"
|
|
#include "lps28dfw.h"
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_DECLARE(LPS2XDF, CONFIG_SENSOR_LOG_LEVEL);
|
|
|
|
static inline int lps28dfw_mode_set_odr_raw(const struct device *dev, uint8_t odr)
|
|
{
|
|
const struct lps2xdf_config *const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_md_t md;
|
|
|
|
md.odr = odr;
|
|
md.avg = cfg->avg;
|
|
md.lpf = cfg->lpf;
|
|
md.fs = cfg->fs;
|
|
|
|
return lps28dfw_mode_set(ctx, &md);
|
|
}
|
|
|
|
static int lps28dfw_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
|
{
|
|
struct lps2xdf_data *data = dev->data;
|
|
const struct lps2xdf_config *const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_data_t raw_data;
|
|
lps28dfw_md_t md;
|
|
|
|
md.fs = cfg->fs;
|
|
|
|
if (lps28dfw_data_get(ctx, &md, &raw_data) < 0) {
|
|
LOG_DBG("Failed to read sample");
|
|
return -EIO;
|
|
}
|
|
|
|
data->sample_press = raw_data.pressure.raw;
|
|
data->sample_temp = raw_data.heat.raw;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_LPS2XDF_TRIGGER
|
|
/**
|
|
* lps28dfw_config_interrupt - config the interrupt mode
|
|
*/
|
|
static int lps28dfw_config_interrupt(const struct device *dev)
|
|
{
|
|
const struct lps2xdf_config *const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_int_mode_t mode;
|
|
|
|
if (lps28dfw_interrupt_mode_get(ctx, &mode) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
mode.drdy_latched = ~cfg->drdy_pulsed;
|
|
|
|
return lps28dfw_interrupt_mode_set(ctx, &mode);
|
|
}
|
|
|
|
/**
|
|
* lps28dfw_handle_interrupt - handle the drdy event
|
|
* read data and call handler if registered any
|
|
*/
|
|
static void lps28dfw_handle_interrupt(const struct device *dev)
|
|
{
|
|
int ret;
|
|
struct lps2xdf_data *lps28dfw = dev->data;
|
|
const struct lps2xdf_config *cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_all_sources_t status;
|
|
|
|
if (lps28dfw_all_sources_get(ctx, &status) < 0) {
|
|
LOG_DBG("failed reading status reg");
|
|
goto exit;
|
|
}
|
|
|
|
if (status.drdy_pres == 0) {
|
|
goto exit; /* spurious interrupt */
|
|
}
|
|
|
|
if (lps28dfw->handler_drdy != NULL) {
|
|
lps28dfw->handler_drdy(dev, lps28dfw->data_ready_trigger);
|
|
}
|
|
|
|
if (ON_I3C_BUS(cfg)) {
|
|
/*
|
|
* I3C IBI does not rely on GPIO.
|
|
* So no need to enable GPIO pin for interrupt trigger.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
exit:
|
|
ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
|
|
GPIO_INT_EDGE_TO_ACTIVE);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s: Not able to configure pin_int", dev->name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lps28dfw_enable_int - enable selected int pin to generate interrupt
|
|
*/
|
|
static int lps28dfw_enable_int(const struct device *dev, int enable)
|
|
{
|
|
const struct lps2xdf_config * const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_pin_int_route_t int_route;
|
|
|
|
/* set interrupt */
|
|
lps28dfw_pin_int_route_get(ctx, &int_route);
|
|
int_route.drdy_pres = enable;
|
|
return lps28dfw_pin_int_route_set(ctx, &int_route);
|
|
}
|
|
|
|
/**
|
|
* lps28dfw_trigger_set - link external trigger to event data ready
|
|
*/
|
|
static int lps28dfw_trigger_set(const struct device *dev,
|
|
const struct sensor_trigger *trig,
|
|
sensor_trigger_handler_t handler)
|
|
{
|
|
struct lps2xdf_data *lps28dfw = dev->data;
|
|
const struct lps2xdf_config * const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_data_t raw_data;
|
|
lps28dfw_md_t md;
|
|
|
|
md.fs = cfg->fs;
|
|
|
|
if (trig->chan != SENSOR_CHAN_ALL) {
|
|
LOG_WRN("trigger set not supported on this channel.");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
lps28dfw->handler_drdy = handler;
|
|
lps28dfw->data_ready_trigger = trig;
|
|
if (handler) {
|
|
/* dummy read: re-trigger interrupt */
|
|
if (lps28dfw_data_get(ctx, &md, &raw_data) < 0) {
|
|
LOG_DBG("Failed to read sample");
|
|
return -EIO;
|
|
}
|
|
return lps28dfw_enable_int(dev, 1);
|
|
} else {
|
|
return lps28dfw_enable_int(dev, 0);
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_LPS2XDF_TRIGGER */
|
|
|
|
const struct lps2xdf_chip_api st_lps28dfw_chip_api = {
|
|
.mode_set_odr_raw = lps28dfw_mode_set_odr_raw,
|
|
.sample_fetch = lps28dfw_sample_fetch,
|
|
#if CONFIG_LPS2XDF_TRIGGER
|
|
.config_interrupt = lps28dfw_config_interrupt,
|
|
.handle_interrupt = lps28dfw_handle_interrupt,
|
|
.trigger_set = lps28dfw_trigger_set,
|
|
#endif
|
|
};
|
|
|
|
int st_lps28dfw_init(const struct device *dev)
|
|
{
|
|
const struct lps2xdf_config *const cfg = dev->config;
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
lps28dfw_id_t id;
|
|
lps28dfw_stat_t status;
|
|
uint8_t tries = 10;
|
|
int ret;
|
|
|
|
#if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps28dfw, i3c)
|
|
if (cfg->i3c.bus != NULL) {
|
|
struct lps2xdf_data *data = dev->data;
|
|
/*
|
|
* Need to grab the pointer to the I3C device descriptor
|
|
* before we can talk to the sensor.
|
|
*/
|
|
data->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
|
|
if (data->i3c_dev == NULL) {
|
|
LOG_ERR("Cannot find I3C device descriptor");
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (lps28dfw_id_get(ctx, &id) < 0) {
|
|
LOG_ERR("%s: Not able to read dev id", dev->name);
|
|
return -EIO;
|
|
}
|
|
|
|
if (id.whoami != LPS28DFW_ID) {
|
|
LOG_ERR("%s: Invalid chip ID 0x%02x", dev->name, id.whoami);
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("%s: chip id 0x%x", dev->name, id.whoami);
|
|
|
|
/* Restore default configuration */
|
|
if (lps28dfw_init_set(ctx, LPS28DFW_RESET) < 0) {
|
|
LOG_ERR("%s: Not able to reset device", dev->name);
|
|
return -EIO;
|
|
}
|
|
|
|
do {
|
|
if (!--tries) {
|
|
LOG_DBG("sw reset timed out");
|
|
return -ETIMEDOUT;
|
|
}
|
|
k_usleep(LPS2XDF_SWRESET_WAIT_TIME_US);
|
|
|
|
if (lps28dfw_status_get(ctx, &status) < 0) {
|
|
return -EIO;
|
|
}
|
|
} while (status.sw_reset);
|
|
|
|
/* Set bdu and if_inc recommended for driver usage */
|
|
if (lps28dfw_init_set(ctx, LPS28DFW_DRV_RDY) < 0) {
|
|
LOG_ERR("%s: Not able to set device to ready state", dev->name);
|
|
return -EIO;
|
|
}
|
|
|
|
if (ON_I3C_BUS(cfg)) {
|
|
lps28dfw_bus_mode_t bus_mode;
|
|
|
|
/* Select bus interface */
|
|
lps28dfw_bus_mode_get(ctx, &bus_mode);
|
|
bus_mode.filter = LPS28DFW_AUTO;
|
|
bus_mode.interface = LPS28DFW_SEL_BY_HW;
|
|
lps28dfw_bus_mode_set(ctx, &bus_mode);
|
|
}
|
|
|
|
/* set sensor default odr */
|
|
LOG_DBG("%s: odr: %d", dev->name, cfg->odr);
|
|
ret = lps28dfw_mode_set_odr_raw(dev, cfg->odr);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s: Failed to set odr %d", dev->name, cfg->odr);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_LPS2XDF_TRIGGER
|
|
if (cfg->trig_enabled) {
|
|
if (lps2xdf_init_interrupt(dev, DEVICE_VARIANT_LPS28DFW) < 0) {
|
|
LOG_ERR("Failed to initialize interrupt.");
|
|
return -EIO;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|