/* * Copyright (c) 2021 Jimmy Johnson * * 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); /** TI conversion scale from 16 bit int temp value to float */ #define TMP108_TEMP_MULTIPLIER 62500 /** TMP typical conversion time of 27 ms after waking from sleep */ #define TMP108_WAKEUP_TIME_IN_MS 30 struct tmp108_config { const struct i2c_dt_spec i2c_spec; const struct gpio_dt_spec alert_gpio; }; 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; int result; if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) { return -ENOTSUP; } /* If one shot mode is set, query chip for reading * should be finished 30 ms later */ if (drv_data->one_shot_mode == true) { result = tmp108_write_config(dev, TI_TMP108_MODE_MASK, TI_TMP108_MODE_ONE_SHOT); if (result < 0) { return result; } /* Schedule read to start in 30 ms if mode change was successful * the typical wakeup time given in the data sheet is 27 */ result = k_work_schedule(&drv_data->scheduled_work, K_MSEC(TMP108_WAKEUP_TIME_IN_MS)); if (result < 0) { return result; } return 0; } result = ti_tmp108_read_temp(dev); if (result < 0) { return result; } return 0; } 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 >> 4U) * TMP108_TEMP_MULTIPLIER; val->val1 = uval / 1000000U; val->val2 = uval % 1000000U; return 0; } static int tmp108_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { int result; 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, (uint16_t *) &(val->val1)); 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; uint16_t mode = 0; uint16_t reg_value = 0; int result = 0; if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) { return -ENOTSUP; } switch ((int) attr) { case SENSOR_ATTR_HYSTERESIS: if (val->val1 < 1) { mode = TI_TMP108_HYSTER_0_C; } else if (val->val1 < 2) { mode = TI_TMP108_HYSTER_1_C; } else if (val->val1 < 4) { mode = TI_TMP108_HYSTER_2_C; } else { mode = TI_TMP108_HYSTER_4_C; } result = tmp108_write_config(dev, TI_TMP108_HYSTER_MASK, 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; } else { mode = TI_TMP108_CONF_TM_CMP; } result = tmp108_write_config(dev, TI_TMP108_CONF_TM_MASK, mode); break; case SENSOR_ATTR_LOWER_THRESH: reg_value = (val->val1 << 8) | (0x00FF & val->val2); result = tmp108_reg_write(dev, TI_TMP108_REG_LOW_LIMIT, reg_value); break; case SENSOR_ATTR_UPPER_THRESH: reg_value = (val->val1 << 8) | (0x00FF & val->val2); result = tmp108_reg_write(dev, TI_TMP108_REG_HIGH_LIMIT, reg_value); break; case SENSOR_ATTR_SAMPLING_FREQUENCY: if (val->val1 < 1) { mode = TI_TMP108_FREQ_4_SECS; } else if (val->val1 < 4) { mode = TI_TMP108_FREQ_1_HZ; } else if (val->val1 < 16) { mode = TI_TMP108_FREQ_4_HZ; } else { mode = TI_TMP108_FREQ_16_HZ; } result = tmp108_write_config(dev, TI_TMP108_FREQ_MASK, mode); break; case SENSOR_ATTR_TMP108_SHUTDOWN_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK, TI_TMP108_MODE_SHUTDOWN); drv_data->one_shot_mode = false; break; case SENSOR_ATTR_TMP108_CONTINUOUS_CONVERSION_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK, TI_TMP108_MODE_CONTINUOUS); drv_data->one_shot_mode = false; break; case SENSOR_ATTR_TMP108_ONE_SHOT_MODE: result = tmp108_write_config(dev, TI_TMP108_MODE_MASK, TI_TMP108_MODE_ONE_SHOT); drv_data->one_shot_mode = true; break; case SENSOR_ATTR_TMP108_ALERT_POLARITY: if (val->val1 == 1) { mode = TI_TMP108_CONF_POL_HIGH; } else { mode = TI_TMP108_CONF_POL_LOW; } result = tmp108_write_config(dev, TI_TMP108_CONF_POL_MASK, mode); break; default: return -ENOTSUP; } if (result < 0) { return result; } return 0; } static const struct sensor_driver_api tmp108_driver_api = { .attr_set = tmp108_attr_set, .attr_get = tmp108_attr_get, .sample_fetch = tmp108_sample_fetch, .channel_get = tmp108_channel_get, .trigger_set = tmp_108_trigger_set, }; #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; struct tmp108_data *drv_data = dev->data; 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; } drv_data->scheduled_work.work.handler = tmp108_trigger_handle_one_shot; /* save this driver instance for passing to other functions */ drv_data->tmp108_dev = dev; #ifdef CONFIG_TMP108_ALERT_INTERRUPTS result = setup_interrupts(dev); #endif return result; } #define TMP108_DEFINE(inst) \ static struct tmp108_data tmp108_prv_data_##inst; \ static const struct tmp108_config tmp108_config_##inst = { \ .i2c_spec = I2C_DT_SPEC_INST_GET(inst), \ .alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, \ alert_gpios, { 0 }) \ }; \ DEVICE_DT_INST_DEFINE(inst, \ &tmp108_init, \ NULL, \ &tmp108_prv_data_##inst, \ &tmp108_config_##inst, \ POST_KERNEL, \ CONFIG_SENSOR_INIT_PRIORITY, \ &tmp108_driver_api); DT_INST_FOREACH_STATUS_OKAY(TMP108_DEFINE)