text: /*
   * Copyright (c) 2019 Centaur Analytics, Inc
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #define DT_DRV_COMPAT ti_tmp116
  
  #include <device.h>
  #include <drivers/i2c.h>
  #include <drivers/sensor.h>
  #include <sys/util.h>
  #include <sys/byteorder.h>
  #include <sys/__assert.h>
  #include <logging/log.h>
  #include <kernel.h>
  
  #include "tmp116.h"
  
  LOG_MODULE_REGISTER(TMP116, CONFIG_SENSOR_LOG_LEVEL);
  
  static int tmp116_reg_read(const struct device *dev, uint8_t reg,
  			   uint16_t *val)
  {
  	const struct tmp116_dev_config *cfg = dev->config;
  
  	if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *)val, 2)
  	    < 0) {
  		return -EIO;
  	}
  
  	*val = sys_be16_to_cpu(*val);
  
  	return 0;
  }
  
  static int tmp116_reg_write(const struct device *dev, uint8_t reg,
  			    uint16_t val)
  {
  	const struct tmp116_dev_config *cfg = dev->config;
  	uint8_t tx_buf[3] = {reg, val >> 8, val & 0xFF};
  
  	return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
  }
  
  /**
   * @brief Check the Device ID
   *
   * @param[in]   dev     Pointer to the device structure
   * @param[in]	id	Pointer to the variable for storing the device id
   *
   * @retval 0 on success
   * @retval -EIO Otherwise
   */
  static inline int tmp116_device_id_check(const struct device *dev, uint16_t *id)
  {
  	if (tmp116_reg_read(dev, TMP116_REG_DEVICE_ID, id) != 0) {
  		LOG_ERR("%s: Failed to get Device ID register!",
  			dev->name);
  		return -EIO;
  	}
  
  	if ((*id != TMP116_DEVICE_ID) && (*id != TMP117_DEVICE_ID)) {
  		LOG_ERR("%s: Failed to match the device IDs!",
  			dev->name);
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  static int tmp116_sample_fetch(const struct device *dev,
  			       enum sensor_channel chan)
  {
  	struct tmp116_data *drv_data = dev->data;
  	uint16_t value;
  	uint16_t cfg_reg = 0;
  	int rc;
  
  	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
  			chan == SENSOR_CHAN_AMBIENT_TEMP);
  
  	/* clear sensor values */
  	drv_data->sample = 0U;
  
  	/* Make sure that a data is available */
  	rc = tmp116_reg_read(dev, TMP116_REG_CFGR, &cfg_reg);
  	if (rc < 0) {
  		LOG_ERR("%s, Failed to read from CFGR register",
  			dev->name);
  		return rc;
  	}
  
  	if ((cfg_reg & TMP116_CFGR_DATA_READY) == 0) {
  		LOG_DBG("%s: no data ready", dev->name);
  		return -EBUSY;
  	}
  
  	/* Get the most recent temperature measurement */
  	rc = tmp116_reg_read(dev, TMP116_REG_TEMP, &value);
  	if (rc < 0) {
  		LOG_ERR("%s: Failed to read from TEMP register!",
  			dev->name);
  		return rc;
  	}
  
  	/* store measurements to the driver */
  	drv_data->sample = (int16_t)value;
  
  	return 0;
  }
  
  static int tmp116_channel_get(const struct device *dev,
  			      enum sensor_channel chan,
  			      struct sensor_value *val)
  {
  	struct tmp116_data *drv_data = dev->data;
  	int32_t tmp;
  
  	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
  		return -ENOTSUP;
  	}
  
  	/*
  	 * See datasheet "Temperature Results and Limits" section for more
  	 * details on processing sample data.
  	 */
  	tmp = ((int16_t)drv_data->sample * (int32_t)TMP116_RESOLUTION) / 10;
  	val->val1 = tmp / 1000000; /* uCelsius */
  	val->val2 = tmp % 1000000;
  
  	return 0;
  }
  
  static int tmp116_attr_set(const struct device *dev,
  			   enum sensor_channel chan,
  			   enum sensor_attribute attr,
  			   const struct sensor_value *val)
  {
  	struct tmp116_data *drv_data = dev->data;
  	int16_t value;
  
  	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
  		return -ENOTSUP;
  	}
  
  	switch (attr) {
  	case SENSOR_ATTR_OFFSET:
  		if (drv_data->id != TMP117_DEVICE_ID) {
  			LOG_ERR("%s: Offset is only supported by TMP117",
  			dev->name);
  			return -EINVAL;
  		}
  		/*
  		 * The offset is encoded into the temperature register format.
  		 */
  		value = (((val->val1) * 10000000) + ((val->val2) * 10))
  						/ (int32_t)TMP116_RESOLUTION;
  
  		return tmp116_reg_write(dev, TMP117_REG_TEMP_OFFSET, value);
  
  	default:
  		return -ENOTSUP;
  	}
  }
  
  static const struct sensor_driver_api tmp116_driver_api = {
  	.attr_set = tmp116_attr_set,
  	.sample_fetch = tmp116_sample_fetch,
  	.channel_get = tmp116_channel_get
  };
  
  static int tmp116_init(const struct device *dev)
  {
  	struct tmp116_data *drv_data = dev->data;
  	const struct tmp116_dev_config *cfg = dev->config;
  	int rc;
  	uint16_t id;
  
  	if (!device_is_ready(cfg->bus.bus)) {
  		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
  		return -EINVAL;
  	}
  
  	/* Check the Device ID */
  	rc = tmp116_device_id_check(dev, &id);
  	if (rc < 0) {
  		return rc;
  	}
  	LOG_DBG("Got device ID: %x", id);
  	drv_data->id = id;
  
  	return 0;
  }
  
  #define DEFINE_TMP116(_num) \
  	static struct tmp116_data tmp116_data_##_num; \
  	static const struct tmp116_dev_config tmp116_config_##_num = { \
  		.bus = I2C_DT_SPEC_INST_GET(_num) \
  	}; \
  	DEVICE_DT_INST_DEFINE(_num, tmp116_init, NULL,			\
  		&tmp116_data_##_num, &tmp116_config_##_num, POST_KERNEL, \
  		CONFIG_SENSOR_INIT_PRIORITY, &tmp116_driver_api);
  
  DT_INST_FOREACH_STATUS_OKAY(DEFINE_TMP116)
  