/* * SPDX-License-Identifier: Apache-2.0 * * Würth Elektronic WSEN-ITDS 3-axis accel sensor driver * * Copyright (c) 2020 Linumiz * Author: Saravanan Sekar */ #include #include #include #include #include #include #include "itds.h" #define DT_DRV_COMPAT we_wsen_itds #define ITDS_TEMP_CONST 62500 LOG_MODULE_REGISTER(ITDS, CONFIG_SENSOR_LOG_LEVEL); static const struct itds_odr itds_odr_map[ITDS_ODR_MAX] = { {0}, {1, 600}, {12, 500}, {25}, {50}, {100}, {200}, {400}, {800}, {1600} }; static const unsigned int itds_sensitivity_scale[][ITDS_ACCL_RANGE_END] = { {976, 1952, 3904, 7808}, /* high performance mode */ {244, 488, 976, 1952} }; static int itds_get_odr_for_index(const struct device *dev, enum itds_odr_const idx, uint16_t *freq, uint16_t *mfreq) { struct itds_device_data *ddata = dev->data; int start, end; bool hp_mode; hp_mode = !!(ddata->op_mode & ITDS_OP_MODE_HIGH_PERF); if (hp_mode) { start = ITDS_ODR_12_5; end = ITDS_ODR_1600; } else { start = ITDS_ODR_1_6; end = ITDS_ODR_200; } if (idx < start || idx > end) { LOG_ERR("invalid odr for the operating mode"); return -EINVAL; } *freq = itds_odr_map[idx].freq; *mfreq = itds_odr_map[idx].mfreq; return 0; } static int itds_accl_odr_set(const struct device *dev, uint16_t freq, uint16_t mfreq) { struct itds_device_data *ddata = dev->data; const struct itds_device_config *cfg = dev->config; int start, end, i; bool hp_mode; hp_mode = !!(ddata->op_mode & ITDS_OP_MODE_HIGH_PERF); if (hp_mode) { start = ITDS_ODR_12_5; end = ITDS_ODR_1600; } else { start = ITDS_ODR_1_6; end = ITDS_ODR_200; } for (i = start; i <= end; i++) { if ((freq == itds_odr_map[i].freq) && (mfreq == itds_odr_map[i].mfreq)) { return i2c_reg_update_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_CTRL1, ITDS_MASK_ODR, i << 4); } } LOG_ERR("invalid odr, not in range"); return -EINVAL; } static int itds_accl_range_set(const struct device *dev, int32_t range) { struct itds_device_data *ddata = dev->data; const struct itds_device_config *cfg = dev->config; int i, ret; bool hp_mode; for (i = 0; i < ITDS_ACCL_RANGE_END; i++) { if (range <= (2 << i)) { break; } } if (i == ITDS_ACCL_RANGE_END) { LOG_ERR("Accl out of range"); return -EINVAL; } ret = i2c_reg_update_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_CTRL6, ITDS_MASK_SCALE, i << 4); if (ret) { LOG_ERR("Accl set full scale failed %d", ret); return ret; } hp_mode = !!(ddata->op_mode & ITDS_OP_MODE_HIGH_PERF); ddata->scale = itds_sensitivity_scale[hp_mode][i]; return 0; } static int itds_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { if (chan != SENSOR_CHAN_ACCEL_X && chan != SENSOR_CHAN_ACCEL_Y && chan != SENSOR_CHAN_ACCEL_Z && chan != SENSOR_CHAN_ACCEL_XYZ) { LOG_ERR("attr_set() not supported on this channel."); return -ENOTSUP; } switch (attr) { case SENSOR_ATTR_FULL_SCALE: return itds_accl_range_set(dev, sensor_ms2_to_g(val)); case SENSOR_ATTR_SAMPLING_FREQUENCY: return itds_accl_odr_set(dev, val->val1, val->val2 / 1000); default: LOG_ERR("Accel attribute not supported."); return -ENOTSUP; } } static int itds_fetch_temprature(struct itds_device_data *ddata, const struct itds_device_config *cfg) { uint8_t rval; int16_t temp_raw = 0; int ret; ret = i2c_reg_read_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_STATUS_DETECT, &rval); if (ret) { return ret; } if (!(rval & ITDS_EVENT_DRDY_T)) { return -EAGAIN; } ret = i2c_burst_read(ddata->i2c, cfg->i2c_addr, ITDS_REG_TEMP_L, (uint8_t *)&temp_raw, sizeof(uint16_t)); if (ret) { return ret; } ddata->temprature = sys_le16_to_cpu(temp_raw); return 0; } static int itds_fetch_accel(struct itds_device_data *ddata, const struct itds_device_config *cfg) { size_t i, ret; uint8_t rval; ret = i2c_reg_read_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_STATUS, &rval); if (ret) { return ret; } if (!(rval & ITDS_EVENT_DRDY)) { return -EAGAIN; } ret = i2c_burst_read(ddata->i2c, cfg->i2c_addr, ITDS_REG_X_OUT_L, (uint8_t *)ddata->samples, sizeof(uint16_t) * ITDS_SAMPLE_SIZE); if (ret) { return ret; } /* convert samples to cpu endianness */ for (i = 0; i < ITDS_SAMPLE_SIZE; i += 2) { int16_t *sample = (int16_t *) &ddata->samples[i]; *sample = sys_le16_to_cpu(*sample); if (ddata->op_mode & ITDS_OP_MODE_NORMAL || ddata->op_mode & ITDS_OP_MODE_HIGH_PERF) { *sample = *sample >> 2; } else { *sample = *sample >> 4; } LOG_DBG("itds sample %d %X\n", i, *sample); } return 0; } static int itds_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct itds_device_data *ddata = dev->data; const struct itds_device_config *cfg = dev->config; switch (chan) { case SENSOR_CHAN_ACCEL_XYZ: case SENSOR_CHAN_ACCEL_X: case SENSOR_CHAN_ACCEL_Y: case SENSOR_CHAN_ACCEL_Z: return itds_fetch_accel(ddata, cfg); case SENSOR_CHAN_DIE_TEMP: return itds_fetch_temprature(ddata, cfg); default: return -EINVAL; } } static inline void itds_accl_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { int i; struct itds_device_data *ddata = dev->data; uint8_t ofs_start, ofs_stop; switch (chan) { case SENSOR_CHAN_ACCEL_X: ofs_start = ofs_stop = 0U; break; case SENSOR_CHAN_ACCEL_Y: ofs_start = ofs_stop = 1U; break; case SENSOR_CHAN_ACCEL_Z: ofs_start = ofs_stop = 2U; break; default: ofs_start = 0U; ofs_stop = 2U; break; } for (i = ofs_start; i <= ofs_stop ; i++, val++) { int64_t dval; /* Sensitivity is exposed in ug/LSB */ /* Convert to m/s^2 */ dval = (int64_t)((ddata->samples[i] * ddata->scale * SENSOR_G) / 1000000LL); val->val1 = (int32_t)(dval / 1000000); val->val2 = (int32_t)(dval % 1000000); } } static int itds_temp_channel_get(const struct device *dev, struct sensor_value *val) { int32_t temp_processed; struct itds_device_data *ddata = dev->data; temp_processed = (ddata->temprature >> 4) * ITDS_TEMP_CONST; val->val1 = ITDS_TEMP_OFFSET; val->val2 = temp_processed; return 0; } static int itds_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { switch (chan) { case SENSOR_CHAN_ACCEL_X: case SENSOR_CHAN_ACCEL_Y: case SENSOR_CHAN_ACCEL_Z: case SENSOR_CHAN_ACCEL_XYZ: itds_accl_channel_get(dev, chan, val); return 0; case SENSOR_CHAN_DIE_TEMP: return itds_temp_channel_get(dev, val); default: LOG_ERR("Channel not supported."); return -ENOTSUP; } return 0; } static int itds_init(const struct device *dev) { struct itds_device_data *ddata = dev->data; const struct itds_device_config *cfg = dev->config; int ret; uint16_t freq, mfreq; uint8_t rval; ddata->i2c = device_get_binding(cfg->bus_name); if (!ddata->i2c) { LOG_ERR("I2C controller not found: %s.", cfg->bus_name); return -EINVAL; } ret = i2c_reg_read_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_DEV_ID, &rval); if (ret) { LOG_ERR("device init fail: %d", ret); return ret; } if (rval != ITDS_DEVICE_ID) { LOG_ERR("device ID mismatch: %x", rval); return ret; } ret = i2c_reg_update_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_CTRL2, ITDS_MASK_BDU_INC_ADD, ITDS_MASK_BDU_INC_ADD); if (ret) { LOG_ERR("unable to set block data update %d", ret); return ret; } ret = i2c_reg_write_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_WAKEUP_EVENT, 0); if (ret) { LOG_ERR("disable wakeup event fail %d", ret); return ret; } ret = i2c_reg_update_byte(ddata->i2c, cfg->i2c_addr, ITDS_REG_CTRL1, ITDS_MASK_MODE, 1 << cfg->def_op_mode); if (ret) { LOG_ERR("set operating mode fail %d", ret); return ret; } ddata->op_mode = 1 << cfg->def_op_mode; ret = itds_get_odr_for_index(dev, cfg->def_odr, &freq, &mfreq); if (ret) { LOG_ERR("odr not in range for operating mode %d", ret); return ret; } ret = itds_accl_odr_set(dev, freq, mfreq); if (ret) { LOG_ERR("odr not in range for operating mode %d", ret); return ret; } #ifdef CONFIG_ITDS_TRIGGER ret = itds_trigger_mode_init(dev); if (ret) { LOG_ERR("trigger mode init failed %d", ret); return ret; } #endif return 0; } static const struct sensor_driver_api itds_api = { .attr_set = itds_attr_set, #ifdef CONFIG_ITDS_TRIGGER .trigger_set = itds_trigger_set, #endif .sample_fetch = itds_sample_fetch, .channel_get = itds_channel_get, }; #define WSEN_ITDS_INIT(idx) \ \ static struct itds_device_data itds_data_##idx; \ \ static const struct itds_device_config itds_config_##idx = { \ .i2c_addr = DT_INST_REG_ADDR(idx), \ .bus_name = DT_INST_BUS_LABEL(idx), \ .gpio_port = DT_INST_GPIO_LABEL(idx, int_gpios), \ .int_pin = DT_INST_GPIO_PIN(idx, int_gpios), \ .int_flags = DT_INST_GPIO_FLAGS(idx, int_gpios), \ .def_odr = DT_ENUM_IDX(DT_DRV_INST(idx), odr), \ .def_op_mode = DT_ENUM_IDX(DT_DRV_INST(idx), op_mode), \ }; \ \ DEVICE_DT_INST_DEFINE(idx, itds_init, device_pm_control_nop, \ &itds_data_##idx, &itds_config_##idx, \ POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ &itds_api); \ DT_INST_FOREACH_STATUS_OKAY(WSEN_ITDS_INIT)