|  | /* | 
|  | * Copyright (c) 2019 Thomas Schmid <tom@lfence.de> | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT meas_ms5607 | 
|  |  | 
|  | #include <init.h> | 
|  | #include <kernel.h> | 
|  | #include <sys/byteorder.h> | 
|  | #include <drivers/sensor.h> | 
|  | #include <sys/__assert.h> | 
|  |  | 
|  | #include "ms5607.h" | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL | 
|  | #include <logging/log.h> | 
|  | LOG_MODULE_REGISTER(ms5607); | 
|  |  | 
|  | static void ms5607_compensate(struct ms5607_data *data, | 
|  | const int32_t adc_temperature, | 
|  | const int32_t adc_pressure) | 
|  | { | 
|  | int64_t dT; | 
|  | int64_t OFF; | 
|  | int64_t SENS; | 
|  | int64_t temp_sq; | 
|  | int64_t Ti; | 
|  | int64_t OFFi; | 
|  | int64_t SENSi; | 
|  |  | 
|  | /* first order compensation as per datasheet | 
|  | * (https://www.te.com/usa-en/product-CAT-BLPS0035.html) section | 
|  | * PRESSURE AND TEMPERATURE CALCULATION | 
|  | */ | 
|  |  | 
|  | dT = adc_temperature - ((int32_t)(data->t_ref) << 8); | 
|  | data->temperature = 2000 + (dT * data->tempsens) / (1ll << 23); | 
|  | OFF = ((int64_t)(data->off_t1) << 17) + (dT * data->tco) / (1ll << 6); | 
|  | SENS = ((int64_t)(data->sens_t1) << 16) + (dT * data->tcs) / (1ll << 7); | 
|  |  | 
|  | /* Second order compensation as per datasheet | 
|  | * (https://www.te.com/usa-en/product-CAT-BLPS0035.html) section | 
|  | * SECOND ORDER TEMPERATURE COMPENSATION | 
|  | */ | 
|  |  | 
|  | temp_sq = (int64_t)(data->temperature - 2000) * | 
|  | (int64_t)(data->temperature - 2000); | 
|  | if (data->temperature < 2000) { | 
|  | Ti = (dT * dT) / (1ll << 31); | 
|  | OFFi = (61ll * temp_sq) / (1ll << 4); | 
|  | SENSi = 2ll * temp_sq; | 
|  | if (data->temperature < -1500) { | 
|  | temp_sq = (int64_t)(data->temperature + 1500) * | 
|  | (int64_t)(data->temperature + 1500); | 
|  | OFFi += 15ll * temp_sq; | 
|  | SENSi += 8ll * temp_sq; | 
|  | } | 
|  | } else { | 
|  | SENSi = 0; | 
|  | OFFi = 0; | 
|  | Ti = 0; | 
|  | } | 
|  |  | 
|  | OFF -= OFFi; | 
|  | SENS -= SENSi; | 
|  |  | 
|  | data->temperature -= Ti; | 
|  | data->pressure = (SENS * (int64_t)adc_pressure / (1ll << 21) - OFF) / | 
|  | (1ll << 15); | 
|  | } | 
|  |  | 
|  | static int ms5607_read_prom(const struct ms5607_config *config, uint8_t cmd, | 
|  | uint16_t *val) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | err = config->tf->read_prom(config, cmd, val); | 
|  | if (err < 0) { | 
|  | LOG_ERR("Error reading prom"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ms5607_get_measurement(const struct ms5607_config *config, | 
|  | uint32_t *val, | 
|  | uint8_t cmd, | 
|  | uint8_t delay) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | *val = 0U; | 
|  |  | 
|  | err = config->tf->start_conversion(config, cmd); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | k_msleep(delay); | 
|  |  | 
|  | err = config->tf->read_adc(config, val); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ms5607_sample_fetch(const struct device *dev, | 
|  | enum sensor_channel channel) | 
|  | { | 
|  | const struct ms5607_config *config = dev->config; | 
|  | struct ms5607_data *data = dev->data; | 
|  | int err; | 
|  | uint32_t adc_pressure, adc_temperature; | 
|  |  | 
|  | __ASSERT_NO_MSG(channel == SENSOR_CHAN_ALL); | 
|  |  | 
|  | err = ms5607_get_measurement(config, | 
|  | &adc_pressure, | 
|  | data->pressure_conv_cmd, | 
|  | data->pressure_conv_delay); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | err = ms5607_get_measurement(config, | 
|  | &adc_temperature, | 
|  | data->temperature_conv_cmd, | 
|  | data->temperature_conv_delay); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | ms5607_compensate(data, adc_temperature, adc_pressure); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ms5607_channel_get(const struct device *dev, | 
|  | enum sensor_channel chan, | 
|  | struct sensor_value *val) | 
|  | { | 
|  | const struct ms5607_data *data = dev->data; | 
|  |  | 
|  | switch (chan) { | 
|  | case SENSOR_CHAN_AMBIENT_TEMP: | 
|  | val->val1 = data->temperature / 100; | 
|  | val->val2 = data->temperature % 100 * 10000; | 
|  | break; | 
|  | case SENSOR_CHAN_PRESS: | 
|  | val->val1 = data->pressure / 100; | 
|  | val->val2 = data->pressure % 100 * 10000; | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ms5607_attr_set(const struct device *dev, enum sensor_channel chan, | 
|  | enum sensor_attribute attr, | 
|  | const struct sensor_value *val) | 
|  | { | 
|  | struct ms5607_data *data = dev->data; | 
|  | uint8_t p_conv_cmd, t_conv_cmd, conv_delay; | 
|  |  | 
|  | if (attr != SENSOR_ATTR_OVERSAMPLING) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | switch (val->val1) { | 
|  | case 4096: | 
|  | p_conv_cmd = MS5607_CMD_CONV_P_4096; | 
|  | t_conv_cmd = MS5607_CMD_CONV_T_4096; | 
|  | conv_delay = 9U; | 
|  | break; | 
|  | case 2048: | 
|  | p_conv_cmd = MS5607_CMD_CONV_P_2048; | 
|  | t_conv_cmd = MS5607_CMD_CONV_T_2048; | 
|  | conv_delay = 5U; | 
|  | break; | 
|  | case 1024: | 
|  | p_conv_cmd = MS5607_CMD_CONV_P_1024; | 
|  | t_conv_cmd = MS5607_CMD_CONV_T_1024; | 
|  | conv_delay = 3U; | 
|  | break; | 
|  | case 512: | 
|  | p_conv_cmd = MS5607_CMD_CONV_P_512; | 
|  | t_conv_cmd = MS5607_CMD_CONV_T_512; | 
|  | conv_delay = 2U; | 
|  | break; | 
|  | case 256: | 
|  | p_conv_cmd = MS5607_CMD_CONV_P_256; | 
|  | t_conv_cmd = MS5607_CMD_CONV_T_256; | 
|  | conv_delay = 1U; | 
|  | break; | 
|  | default: | 
|  | LOG_ERR("invalid oversampling rate %d", val->val1); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (chan) { | 
|  | case SENSOR_CHAN_ALL: | 
|  | data->pressure_conv_cmd = p_conv_cmd; | 
|  | data->temperature_conv_cmd = t_conv_cmd; | 
|  | data->temperature_conv_delay = conv_delay; | 
|  | data->pressure_conv_delay = conv_delay; | 
|  | break; | 
|  | case SENSOR_CHAN_PRESS: | 
|  | data->pressure_conv_cmd = p_conv_cmd; | 
|  | data->pressure_conv_delay = conv_delay; | 
|  | break; | 
|  | case SENSOR_CHAN_AMBIENT_TEMP: | 
|  | data->temperature_conv_cmd = t_conv_cmd; | 
|  | data->temperature_conv_delay = conv_delay; | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ms5607_init(const struct device *dev) | 
|  | { | 
|  | const struct ms5607_config *const config = dev->config; | 
|  | struct ms5607_data *data = dev->data; | 
|  | struct sensor_value val; | 
|  | int err; | 
|  |  | 
|  | err = config->tf->bus_check(config); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | data->pressure = 0; | 
|  | data->temperature = 0; | 
|  |  | 
|  |  | 
|  | val.val1 = MS5607_PRES_OVER_DEFAULT; | 
|  | err = ms5607_attr_set(dev, SENSOR_CHAN_PRESS, SENSOR_ATTR_OVERSAMPLING, | 
|  | &val); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | val.val1 = MS5607_TEMP_OVER_DEFAULT; | 
|  | err = ms5607_attr_set(dev, SENSOR_CHAN_AMBIENT_TEMP, | 
|  | SENSOR_ATTR_OVERSAMPLING, &val); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | err = config->tf->reset(config); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | k_sleep(K_MSEC(2)); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_OFF_T1, | 
|  | &data->off_t1); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("OFF_T1: %d", data->off_t1); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_SENSE_T1, | 
|  | &data->sens_t1); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("SENSE_T1: %d", data->sens_t1); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_T_REF, &data->t_ref); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("T_REF: %d", data->t_ref); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TCO, &data->tco); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("TCO: %d", data->tco); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TCS, &data->tcs); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("TCS: %d", data->tcs); | 
|  |  | 
|  | err = ms5607_read_prom(config, MS5607_CMD_CONV_READ_TEMPSENS, | 
|  | &data->tempsens); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | LOG_DBG("TEMPSENS: %d", data->tempsens); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct sensor_driver_api ms5607_api_funcs = { | 
|  | .attr_set = ms5607_attr_set, | 
|  | .sample_fetch = ms5607_sample_fetch, | 
|  | .channel_get = ms5607_channel_get, | 
|  | }; | 
|  |  | 
|  | #define MS5607_SPI_OPERATION (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |	\ | 
|  | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_TRANSFER_MSB) | 
|  |  | 
|  | /* Initializes a struct ms5607_config for an instance on a SPI bus. */ | 
|  | #define MS5607_CONFIG_SPI(inst)						\ | 
|  | {								\ | 
|  | .bus = DEVICE_DT_GET(DT_INST_BUS(inst)),		\ | 
|  | .tf = &ms5607_spi_transfer_function,			\ | 
|  | .bus_cfg.spi_bus =					\ | 
|  | SPI_DT_SPEC_INST_GET(inst,			\ | 
|  | MS5607_SPI_OPERATION,	\ | 
|  | 0),				\ | 
|  | } | 
|  |  | 
|  | /* Initializes a struct ms5607_config for an instance on a I2C bus. */ | 
|  | #define MS5607_CONFIG_I2C(inst)						\ | 
|  | {								\ | 
|  | .bus = DEVICE_DT_GET(DT_INST_BUS(inst)),		\ | 
|  | .tf = &ms5607_i2c_transfer_function,			\ | 
|  | .bus_cfg.i2c_addr = DT_INST_REG_ADDR(inst)		\ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Main instantiation macro, which selects the correct bus-specific | 
|  | * instantiation macros for the instance. | 
|  | */ | 
|  | #define MS5607_DEFINE(inst)						\ | 
|  | static struct ms5607_data ms5607_data_##inst;			\ | 
|  | static const struct ms5607_config ms5607_config_##inst =	\ | 
|  | COND_CODE_1(DT_INST_ON_BUS(inst, spi),			\ | 
|  | (MS5607_CONFIG_SPI(inst)),			\ | 
|  | (MS5607_CONFIG_I2C(inst)));			\ | 
|  | DEVICE_DT_INST_DEFINE(inst,					\ | 
|  | ms5607_init,					\ | 
|  | NULL,						\ | 
|  | &ms5607_data_##inst,				\ | 
|  | &ms5607_config_##inst,				\ | 
|  | POST_KERNEL,					\ | 
|  | CONFIG_SENSOR_INIT_PRIORITY,			\ | 
|  | &ms5607_api_funcs); | 
|  |  | 
|  | /* Create the struct device for every status "okay" node in the devicetree. */ | 
|  | DT_INST_FOREACH_STATUS_OKAY(MS5607_DEFINE) |