blob: 41f69fd3646f98c71b56485788aab59ea10212bc [file] [log] [blame]
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2023 Linumiz
*/
#define DT_DRV_COMPAT memsic_mc3419
#include <zephyr/init.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include "mc3419.h"
LOG_MODULE_REGISTER(MC3419, CONFIG_SENSOR_LOG_LEVEL);
static const uint16_t mc3419_accel_sense_map[] = {1, 2, 4, 8, 6};
static struct mc3419_odr_map odr_map_table[] = {
{25}, {50}, {62, 500}, {100},
{125}, {250}, {500}, {1000}
};
static int mc3419_get_odr_value(uint16_t freq, uint16_t m_freq)
{
for (int i = 0; i < ARRAY_SIZE(odr_map_table); i++) {
if (odr_map_table[i].freq == freq &&
odr_map_table[i].mfreq == m_freq) {
return i;
}
}
return -EINVAL;
}
static inline int mc3419_set_op_mode(const struct mc3419_config *cfg,
enum mc3419_op_mode mode)
{
return i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_OP_MODE, mode);
}
static int mc3419_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
int ret = 0;
const struct mc3419_config *cfg = dev->config;
struct mc3419_driver_data *data = dev->data;
k_sem_take(&data->sem, K_FOREVER);
ret = i2c_burst_read_dt(&cfg->i2c, MC3419_REG_XOUT_L,
(uint8_t *)data->samples,
MC3419_SAMPLE_READ_SIZE);
k_sem_give(&data->sem);
return ret;
}
static int mc3419_to_sensor_value(double sensitivity, int16_t *raw_data,
struct sensor_value *val)
{
double value = sys_le16_to_cpu(*raw_data);
value *= sensitivity * SENSOR_GRAVITY_DOUBLE / 1000;
return sensor_value_from_double(val, value);
}
static int mc3419_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
int ret = 0;
struct mc3419_driver_data *data = dev->data;
k_sem_take(&data->sem, K_FOREVER);
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[0], val);
break;
case SENSOR_CHAN_ACCEL_Y:
ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[1], val);
break;
case SENSOR_CHAN_ACCEL_Z:
ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[2], val);
break;
case SENSOR_CHAN_ACCEL_XYZ:
ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[0], &val[0]);
ret |= mc3419_to_sensor_value(data->sensitivity, &data->samples[1], &val[1]);
ret |= mc3419_to_sensor_value(data->sensitivity, &data->samples[2], &val[2]);
break;
default:
LOG_ERR("Unsupported channel");
ret = -ENOTSUP;
}
k_sem_give(&data->sem);
return ret;
}
static int mc3419_set_accel_range(const struct device *dev, uint8_t range)
{
int ret = 0;
const struct mc3419_config *cfg = dev->config;
struct mc3419_driver_data *data = dev->data;
if (range >= MC3419_ACCL_RANGE_END) {
LOG_ERR("Accel resolution is out of range");
return -EINVAL;
}
ret = i2c_reg_update_byte_dt(&cfg->i2c, MC3419_REG_RANGE_SELECT_CTRL,
MC3419_RANGE_MASK, range << 4);
if (ret < 0) {
LOG_ERR("Failed to set resolution (%d)", ret);
return ret;
}
data->sensitivity = (double)(mc3419_accel_sense_map[range] *
SENSOR_GRAIN_VALUE);
return 0;
}
static int mc3419_set_odr(const struct device *dev,
const struct sensor_value *val)
{
int ret = 0;
int data_rate = 0;
const struct mc3419_config *cfg = dev->config;
ret = mc3419_get_odr_value(val->val1, val->val2);
if (ret < 0) {
LOG_ERR("Failed to get odr value from odr map (%d)", ret);
return ret;
}
data_rate = MC3419_BASE_ODR_VAL + ret;
ret = i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_SAMPLE_RATE,
data_rate);
if (ret < 0) {
LOG_ERR("Failed to set ODR (%d)", ret);
return ret;
}
LOG_DBG("Set ODR Rate to 0x%x", data_rate);
ret = i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_SAMPLE_RATE_2,
CONFIG_MC3419_DECIMATION_RATE);
if (ret < 0) {
LOG_ERR("Failed to set decimation rate (%d)", ret);
return ret;
}
return 0;
}
#if defined(CONFIG_MC3419_TRIGGER)
static int mc3419_set_anymotion_threshold(const struct device *dev,
const struct sensor_value *val)
{
int ret = 0;
const struct mc3419_config *cfg = dev->config;
uint8_t buf[3] = {0};
if (val->val1 > MC3419_ANY_MOTION_THRESH_MAX) {
return -EINVAL;
}
buf[0] = MC3419_REG_ANY_MOTION_THRES;
sys_put_le16((uint16_t)val->val1, &buf[1]);
ret = i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
if (ret < 0) {
LOG_ERR("Failed to set anymotion threshold (%d)", ret);
return ret;
}
return 0;
}
static int mc3419_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
int ret = 0;
const struct mc3419_config *cfg = dev->config;
struct mc3419_driver_data *data = dev->data;
k_sem_take(&data->sem, K_FOREVER);
ret = mc3419_set_op_mode(cfg, MC3419_MODE_STANDBY);
if (ret < 0) {
goto exit;
}
ret = mc3419_configure_trigger(dev, trig, handler);
if (ret < 0) {
LOG_ERR("Failed to set trigger (%d)", ret);
}
exit:
mc3419_set_op_mode(cfg, MC3419_MODE_WAKE);
k_sem_give(&data->sem);
return ret;
}
#endif
static int mc3419_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
int ret = 0;
struct mc3419_driver_data *data = dev->data;
if (chan != SENSOR_CHAN_ACCEL_X &&
chan != SENSOR_CHAN_ACCEL_Y &&
chan != SENSOR_CHAN_ACCEL_Z &&
chan != SENSOR_CHAN_ACCEL_XYZ) {
LOG_ERR("Not supported on this channel.");
return -ENOTSUP;
}
k_sem_take(&data->sem, K_FOREVER);
ret = mc3419_set_op_mode(dev->config, MC3419_MODE_STANDBY);
if (ret < 0) {
goto exit;
}
switch (attr) {
case SENSOR_ATTR_FULL_SCALE:
ret = mc3419_set_accel_range(dev, val->val1);
break;
case SENSOR_ATTR_SAMPLING_FREQUENCY:
ret = mc3419_set_odr(dev, val);
break;
#if defined(CONFIG_MC3419_TRIGGER)
case SENSOR_ATTR_SLOPE_TH:
ret = mc3419_set_anymotion_threshold(dev, val);
break;
#endif
default:
LOG_ERR("ACCEL attribute is not supported");
ret = -EINVAL;
}
exit:
mc3419_set_op_mode(dev->config, MC3419_MODE_WAKE);
k_sem_give(&data->sem);
return ret;
}
static int mc3419_init(const struct device *dev)
{
int ret = 0;
struct mc3419_driver_data *data = dev->data;
const struct mc3419_config *cfg = dev->config;
if (!(i2c_is_ready_dt(&cfg->i2c))) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}
k_sem_init(&data->sem, 1, 1);
#if defined(CONFIG_MC3419_TRIGGER)
ret = mc3419_trigger_init(dev);
if (ret < 0) {
LOG_ERR("Could not initialize interrupts");
return ret;
}
#endif
/* Leave the sensor in default power on state, will be
* enabled by configure attr or setting trigger.
*/
LOG_INF("MC3419 Initialized");
return ret;
}
static const struct sensor_driver_api mc3419_api = {
.attr_set = mc3419_attr_set,
#if defined(CONFIG_MC3419_TRIGGER)
.trigger_set = mc3419_trigger_set,
#endif
.sample_fetch = mc3419_sample_fetch,
.channel_get = mc3419_channel_get,
};
#if defined(CONFIG_MC3419_TRIGGER)
#define MC3419_CFG_IRQ(idx) \
.int_gpio = GPIO_DT_SPEC_INST_GET_OR(idx, int_gpios, { 0 }), \
.int_cfg = DT_INST_PROP(idx, int_pin2),
#else
#define MC3419_CFG_IRQ(idx)
#endif
#define MC3419_DEFINE(idx) \
static const struct mc3419_config mc3419_config_##idx = { \
.i2c = I2C_DT_SPEC_INST_GET(idx), \
MC3419_CFG_IRQ(idx) \
}; \
static struct mc3419_driver_data mc3419_data_##idx; \
SENSOR_DEVICE_DT_INST_DEFINE(idx, \
mc3419_init, NULL, \
&mc3419_data_##idx, \
&mc3419_config_##idx, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&mc3419_api);
DT_INST_FOREACH_STATUS_OKAY(MC3419_DEFINE)