/* * Copyright (c) 2021 Jimmy Johnson * Copyright (c) 2022 T-Mobile USA, Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ti_tmp108 #include #include #include #include #include #include #include #include "tmp108.h" LOG_MODULE_REGISTER(TMP108, CONFIG_SENSOR_LOG_LEVEL); int tmp108_reg_read(const struct device *dev, uint8_t reg, uint16_t *val) { const struct tmp108_config *cfg = dev->config; int result; result = i2c_burst_read_dt(&cfg->i2c_spec, reg, (uint8_t *) val, 2); if (result < 0) { return result; } *val = sys_be16_to_cpu(*val); return 0; } int tmp108_reg_write(const struct device *dev, uint8_t reg, uint16_t val) { const struct tmp108_config *cfg = dev->config; uint8_t tx_buf[3]; int result; tx_buf[0] = reg; sys_put_be16(val, &tx_buf[1]); result = i2c_write_dt(&cfg->i2c_spec, tx_buf, sizeof(tx_buf)); if (result < 0) { return result; } return 0; } int tmp108_write_config(const struct device *dev, uint16_t mask, uint16_t conf) { uint16_t config = 0; int result; result = tmp108_reg_read(dev, TI_TMP108_REG_CONF, &config); if (result < 0) { return result; } config &= mask; config |= conf; result = tmp108_reg_write(dev, TI_TMP108_REG_CONF, config); if (result < 0) { return result; } return 0; } int ti_tmp108_read_temp(const struct device *dev) { struct tmp108_data *drv_data = dev->data; int result; /* clear previous temperature readings */ drv_data->sample = 0U; /* Get the most recent temperature measurement */ result = tmp108_reg_read(dev, TI_TMP108_REG_TEMP, &drv_data->sample); if (result < 0) { return result; } return 0; } static int tmp108_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct tmp108_data *drv_data = dev->data; uint16_t config, converting_mask; int result; if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) { return -ENOTSUP; } if (!drv_data->one_shot_mode) { /* Read the latest temperature result */ return ti_tmp108_read_temp(dev); } /* Trigger the conversion */ result = tmp108_write_config(dev, TI_TMP108_MODE_MASK(dev), TI_TMP108_MODE_ONE_SHOT(dev)); if (result < 0) { return result; } /* Typical conversion time: * TMP108: 27ms * AS6212: 36ms * Maximum conversion time: * TMP108: 35ms * AS6212: 51ms */ const uint32_t conv_time_min = 25; const uint32_t conv_time_max = 100; const uint32_t poll_period = 5; k_sleep(K_MSEC(conv_time_min)); converting_mask = TI_TMP108_CONF_M1(dev) | TI_TMP108_CONF_M0(dev); for (int i = conv_time_min; i < conv_time_max; i += poll_period) { /* Read the config register */ result = tmp108_reg_read(dev, TI_TMP108_REG_CONF, &config); if (result < 0) { return result; } if ((config & converting_mask) == 0) { /* Conversion has finished */ LOG_DBG("Conversion complete after %d ms", i); return ti_tmp108_read_temp(dev); } /* Wait before reading again */ k_sleep(K_MSEC(poll_period)); } /* Conversion timed out */ return -EAGAIN; } static int tmp108_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct tmp108_data *drv_data = dev->data; int32_t uval; if (chan != SENSOR_CHAN_AMBIENT_TEMP) { return -ENOTSUP; } uval = ((int32_t)drv_data->sample * TMP108_TEMP_MULTIPLIER(dev)) / TMP108_TEMP_DIVISOR(dev); return sensor_value_from_micro(val, uval); } static int tmp108_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { int result; uint16_t tmp_val; if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) { return -ENOTSUP; } switch ((int) attr) { case SENSOR_ATTR_CONFIGURATION: result = tmp108_reg_read(dev, TI_TMP108_REG_CONF, &tmp_val); val->val1 = tmp_val; val->val2 = 0; break; default: return -ENOTSUP; } return result; } static int tmp108_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { struct tmp108_data *drv_data = dev->data; const struct tmp108_config *config = dev->config; __maybe_unused uint16_t reg_value; __maybe_unused int32_t uval; uint16_t mode; int result = 0; if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) { return -ENOTSUP; } switch ((int) attr) { #ifdef CONFIG_TMP108_ALERT_INTERRUPTS case SENSOR_ATTR_HYSTERESIS: if (TI_TMP108_HYSTER_0_C(dev) == TI_TMP108_CONF_NA) { LOG_WRN("AS621x Series lacks Hysterisis setttings"); return -ENOTSUP; } if (val->val1 < 1) { mode = TI_TMP108_HYSTER_0_C(dev); } else if (val->val1 < 2) { mode = TI_TMP108_HYSTER_1_C(dev); } else if (val->val1 < 4) { mode = TI_TMP108_HYSTER_2_C(dev); } else { mode = TI_TMP108_HYSTER_4_C(dev); } result = tmp108_write_config(dev, TI_TMP108_HYSTER_MASK(dev), mode); break; case SENSOR_ATTR_ALERT: /* Spec Sheet Errata: TM is set on reset not cleared */ if (val->val1 == 1) { mode = TI_TMP108_CONF_TM_INT(dev); } else { mode = TI_TMP108_CONF_TM_CMP(dev); } result = tmp108_write_config(dev, TI_TMP108_CONF_TM_MASK(dev), mode); break; case SENSOR_ATTR_LOWER_THRESH: uval = sensor_value_to_micro(val); reg_value = (uval * TMP108_TEMP_DIVISOR(dev)) / TMP108_TEMP_MULTIPLIER(dev); result = tmp108_reg_write(dev, TI_TMP108_REG_LOW_LIMIT, reg_value); break; case SENSOR_ATTR_UPPER_THRESH: uval = sensor_value_to_micro(val); reg_value = (uval * TMP108_TEMP_DIVISOR(dev)) / TMP108_TEMP_MULTIPLIER(dev); result = tmp108_reg_write(dev, TI_TMP108_REG_HIGH_LIMIT, reg_value); break; case SENSOR_ATTR_TMP108_ALERT_POLARITY: if (val->val1 == 1) { mode = TI_TMP108_CONF_POL_HIGH(dev); } else { mode = TI_TMP108_CONF_POL_LOW(dev); } result = tmp108_write_config(dev, TI_TMP108_CONF_POL_MASK(dev), mode); break; #endif /* CONFIG_TMP108_ALERT_INTERRUPTS */ case SENSOR_ATTR_SAMPLING_FREQUENCY: { struct tmp_108_reg_def ams_as6212_reg_def = AMS_AS6212_CONF; if (memcmp(&config->reg_def, &ams_as6212_reg_def, sizeof(struct tmp_108_reg_def)) == 0) { if (val->val1 < 1) { mode = TI_TMP108_FREQ_4_SECS(dev); } else if (val->val1 < 4) { mode = TI_TMP108_FREQ_1_HZ(dev); } else if (val->val1 < 8) { mode = TI_TMP108_FREQ_4_HZ(dev); } else { mode = AMS_AS6212_FREQ_8_HZ(dev); } } else { if (val->val1 < 1) { mode = TI_TMP108_FREQ_4_SECS(dev); } else if (val->val1 < 4) { mode = TI_TMP108_FREQ_1_HZ(dev); } else if (val->val1 < 16) { mode = TI_TMP108_FREQ_4_HZ(dev); } else { mode = TI_TMP108_FREQ_16_HZ(dev); } } result = tmp108_write_config(dev, TI_TMP108_FREQ_MASK(dev), mode); break; } case SENSOR_ATTR_TMP108_SHUTDOWN_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK(dev), TI_TMP108_MODE_SHUTDOWN(dev)); drv_data->one_shot_mode = false; break; case SENSOR_ATTR_TMP108_CONTINUOUS_CONVERSION_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK(dev), TI_TMP108_MODE_CONTINUOUS(dev)); drv_data->one_shot_mode = false; break; case SENSOR_ATTR_TMP108_ONE_SHOT_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK(dev), TI_TMP108_MODE_ONE_SHOT(dev)); drv_data->one_shot_mode = true; break; default: return -ENOTSUP; } if (result < 0) { return result; } return 0; } static DEVICE_API(sensor, tmp108_driver_api) = { .attr_set = tmp108_attr_set, .attr_get = tmp108_attr_get, .sample_fetch = tmp108_sample_fetch, .channel_get = tmp108_channel_get, #ifdef CONFIG_TMP108_ALERT_INTERRUPTS .trigger_set = tmp_108_trigger_set, #endif }; #ifdef CONFIG_TMP108_ALERT_INTERRUPTS static int setup_interrupts(const struct device *dev) { struct tmp108_data *drv_data = dev->data; const struct tmp108_config *config = dev->config; const struct gpio_dt_spec *alert_gpio = &config->alert_gpio; int result; if (!device_is_ready(alert_gpio->port)) { LOG_ERR("tmp108: gpio controller %s not ready", alert_gpio->port->name); return -ENODEV; } result = gpio_pin_configure_dt(alert_gpio, GPIO_INPUT); if (result < 0) { return result; } gpio_init_callback(&drv_data->temp_alert_gpio_cb, tmp108_trigger_handle_alert, BIT(alert_gpio->pin)); result = gpio_add_callback(alert_gpio->port, &drv_data->temp_alert_gpio_cb); if (result < 0) { return result; } result = gpio_pin_interrupt_configure_dt(alert_gpio, GPIO_INT_EDGE_BOTH); if (result < 0) { return result; } return 0; } #endif static int tmp108_init(const struct device *dev) { const struct tmp108_config *cfg = dev->config; int result = 0; if (!device_is_ready(cfg->i2c_spec.bus)) { LOG_ERR("I2C dev %s not ready", cfg->i2c_spec.bus->name); return -ENODEV; } #ifdef CONFIG_TMP108_ALERT_INTERRUPTS struct tmp108_data *drv_data = dev->data; /* save this driver instance for passing to other functions */ drv_data->tmp108_dev = dev; result = setup_interrupts(dev); if (result < 0) { return result; } #endif /* clear and set configuration registers back to default values */ result = tmp108_write_config(dev, 0x0000, TMP108_CONF_RST(dev)); return result; } #define TMP108_DEFINE(inst, t) \ static struct tmp108_data tmp108_prv_data_##inst##t; \ static const struct tmp108_config tmp108_config_##inst##t = { \ .i2c_spec = I2C_DT_SPEC_INST_GET(inst), \ IF_ENABLED(CONFIG_TMP108_ALERT_INTERRUPTS, \ (.alert_gpio = GPIO_DT_SPEC_INST_GET(inst, alert_gpios),)) \ .reg_def = t##_CONF}; \ SENSOR_DEVICE_DT_INST_DEFINE(inst, &tmp108_init, NULL, &tmp108_prv_data_##inst##t, \ &tmp108_config_##inst##t, POST_KERNEL, \ CONFIG_SENSOR_INIT_PRIORITY, &tmp108_driver_api); #define TMP108_INIT(n) TMP108_DEFINE(n, TI_TMP108) #undef DT_DRV_COMPAT #define DT_DRV_COMPAT ti_tmp108 DT_INST_FOREACH_STATUS_OKAY(TMP108_INIT) #define AS6212_INIT(n) TMP108_DEFINE(n, AMS_AS6212) #undef DT_DRV_COMPAT #define DT_DRV_COMPAT ams_as6212 DT_INST_FOREACH_STATUS_OKAY(AS6212_INIT)