| /* |
| * Copyright (c) 2024 Arrow Electronics. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ti_tmp1075 |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/sys/util.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/sensor.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/logging/log.h> |
| |
| #include "tmp1075.h" |
| |
| LOG_MODULE_REGISTER(TMP1075, CONFIG_SENSOR_LOG_LEVEL); |
| |
| #define I2C_REG_ADDR_SIZE 1 |
| #define I2C_REG_SENSOR_SIZE sizeof(uint16_t) |
| #define I2C_BUFFER_SIZE I2C_REG_ADDR_SIZE + I2C_REG_SENSOR_SIZE |
| |
| #define I2C_REG_ADDR_OFFSET 0 |
| #define I2C_WRITE_DATA_OFFSET 1 |
| |
| static int tmp1075_reg_read(const struct tmp1075_config *cfg, uint8_t reg, uint16_t *val) |
| { |
| if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *)val, sizeof(*val)) < 0) { |
| return -EIO; |
| } |
| *val = sys_be16_to_cpu(*val); |
| return 0; |
| } |
| |
| static int tmp1075_reg_write(const struct tmp1075_config *cfg, uint8_t reg, uint16_t val) |
| { |
| uint8_t buf[I2C_REG_ADDR_SIZE + I2C_REG_SENSOR_SIZE]; |
| |
| buf[I2C_REG_ADDR_OFFSET] = reg; |
| sys_put_be16(val, &buf[I2C_WRITE_DATA_OFFSET]); |
| |
| return i2c_write_dt(&cfg->bus, buf, sizeof(buf)); |
| } |
| |
| static inline uint32_t tmp1075_conv_time_ms(uint8_t cr_idx) |
| { |
| switch (cr_idx) { |
| case 0: /* 00: 27.5 ms */ |
| return 28; |
| case 1: /* 01: 55 ms */ |
| return 55; |
| case 2: /* 10: 110 ms */ |
| return 110; |
| case 3: /* 11: 220 ms */ |
| return 220; |
| default: |
| __ASSERT_NO_MSG(false); |
| return 0; |
| } |
| } |
| |
| #if CONFIG_TMP1075_ALERT_INTERRUPTS |
| static int set_threshold_attribute(const struct device *dev, uint8_t reg, int16_t value, |
| const char *error_msg) |
| { |
| if (tmp1075_reg_write(dev->config, reg, value) < 0) { |
| LOG_ERR("Failed to set %s attribute!", error_msg); |
| return -EIO; |
| } |
| return 0; |
| } |
| #endif |
| |
| static int tmp1075_attr_set(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, const struct sensor_value *val) |
| { |
| if (chan != SENSOR_CHAN_AMBIENT_TEMP) { |
| return -ENOTSUP; |
| } |
| |
| switch (attr) { |
| #if CONFIG_TMP1075_ALERT_INTERRUPTS |
| int integer, frac; |
| |
| case SENSOR_ATTR_LOWER_THRESH: |
| /* Extract integer and fractional parts from TMP1075 12-bit register value */ |
| integer = (val->val1 << TMP1075_DATA_INTE_SHIFT) & TMP1075_DATA_INTE_MASK; |
| frac = ((val->val2 / TMP1075_TEMP_SCALE) << TMP1075_DATA_FRAC_SHIFT) & |
| TMP1075_DATA_FRAC_MASK; |
| return set_threshold_attribute(dev, TMP1075_REG_TLOW, integer | frac, |
| "SENSOR_ATTR_LOWER_THRESH"); |
| |
| case SENSOR_ATTR_UPPER_THRESH: |
| /* Extract integer and fractional parts from TMP1075 12-bit register value */ |
| integer = (val->val1 << TMP1075_DATA_INTE_SHIFT) & TMP1075_DATA_INTE_MASK; |
| frac = ((val->val2 / TMP1075_TEMP_SCALE) << TMP1075_DATA_FRAC_SHIFT) & |
| TMP1075_DATA_FRAC_MASK; |
| return set_threshold_attribute(dev, TMP1075_REG_THIGH, integer | frac, |
| "SENSOR_ATTR_UPPER_THRESH"); |
| #endif |
| |
| default: |
| return -ENOTSUP; |
| } |
| } |
| |
| #if CONFIG_TMP1075_ALERT_INTERRUPTS |
| static int get_threshold_attribute(const struct device *dev, uint8_t reg, struct sensor_value *val, |
| const char *error_msg) |
| { |
| uint16_t value; |
| |
| if (tmp1075_reg_read(dev->config, reg, &value) < 0) { |
| LOG_ERR("Failed to get %s attribute!", error_msg); |
| return -EIO; |
| } |
| val->val1 = (value & TMP1075_DATA_INTE_MASK) >> TMP1075_DATA_INTE_SHIFT; |
| val->val2 = |
| ((value & TMP1075_DATA_FRAC_MASK) >> TMP1075_DATA_FRAC_SHIFT) * TMP1075_TEMP_SCALE; |
| return 0; |
| } |
| #endif |
| |
| static int tmp1075_attr_get(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, struct sensor_value *val) |
| { |
| if (chan != SENSOR_CHAN_AMBIENT_TEMP) { |
| return -ENOTSUP; |
| } |
| |
| switch (attr) { |
| #if CONFIG_TMP1075_ALERT_INTERRUPTS |
| case SENSOR_ATTR_LOWER_THRESH: |
| return get_threshold_attribute(dev, TMP1075_REG_TLOW, val, |
| "SENSOR_ATTR_LOWER_THRESH"); |
| |
| case SENSOR_ATTR_UPPER_THRESH: |
| return get_threshold_attribute(dev, TMP1075_REG_THIGH, val, |
| "SENSOR_ATTR_UPPER_THRESH"); |
| #endif |
| |
| default: |
| return -ENOTSUP; |
| } |
| } |
| |
| static int tmp1075_sample_fetch(const struct device *dev, enum sensor_channel chan) |
| { |
| struct tmp1075_data *drv_data = dev->data; |
| const struct tmp1075_config *cfg = dev->config; |
| uint16_t val; |
| |
| __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP); |
| |
| if (cfg->shutdown_mode || cfg->one_shot) { |
| /* Initiate a single temperature conversion */ |
| uint16_t config_reg = drv_data->config_reg; |
| |
| TMP1075_SET_ONE_SHOT_CONVERSION(config_reg, 1); |
| |
| if (tmp1075_reg_write(cfg, TMP1075_REG_CONFIG, config_reg) < 0) { |
| LOG_ERR("Failed to initiate one-shot conversion"); |
| return -EIO; |
| } |
| |
| /* Wait for conversion to complete */ |
| k_sleep(K_MSEC(tmp1075_conv_time_ms(cfg->cr))); |
| } |
| |
| if (tmp1075_reg_read(cfg, TMP1075_REG_TEMPERATURE, &val) < 0) { |
| return -EIO; |
| } |
| drv_data->sample = arithmetic_shift_right((int16_t)val, TMP1075_DATA_NORMAL_SHIFT); |
| return 0; |
| } |
| |
| static int tmp1075_channel_get(const struct device *dev, enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| struct tmp1075_data *drv_data = dev->data; |
| int32_t uval; |
| |
| if (chan != SENSOR_CHAN_AMBIENT_TEMP) { |
| return -ENOTSUP; |
| } |
| |
| uval = (int32_t)drv_data->sample * TMP1075_TEMP_SCALE; |
| val->val1 = uval / uCELSIUS_IN_CELSIUS; |
| val->val2 = uval % uCELSIUS_IN_CELSIUS; |
| |
| return 0; |
| } |
| |
| static DEVICE_API(sensor, tmp1075_driver_api) = { |
| .attr_set = tmp1075_attr_set, |
| .attr_get = tmp1075_attr_get, |
| .sample_fetch = tmp1075_sample_fetch, |
| .channel_get = tmp1075_channel_get, |
| #ifdef CONFIG_TMP1075_ALERT_INTERRUPTS |
| .trigger_set = tmp1075_trigger_set, |
| #endif |
| }; |
| |
| #ifdef CONFIG_TMP1075_ALERT_INTERRUPTS |
| static int setup_interrupts(const struct device *dev) |
| { |
| struct tmp1075_data *drv_data = dev->data; |
| const struct tmp1075_config *config = dev->config; |
| const struct gpio_dt_spec *alert_gpio = &config->alert_gpio; |
| int result; |
| |
| if (!gpio_is_ready_dt(alert_gpio)) { |
| LOG_ERR("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, tmp1075_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 tmp1075_init(const struct device *dev) |
| { |
| const struct tmp1075_config *cfg = dev->config; |
| struct tmp1075_data *data = dev->data; |
| |
| if (!i2c_is_ready_dt(&cfg->bus)) { |
| LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name); |
| return -EINVAL; |
| } |
| #ifdef CONFIG_TMP1075_ALERT_INTERRUPTS |
| int result = setup_interrupts(dev); |
| |
| if (result < 0) { |
| LOG_ERR("Couldn't setup interrupts"); |
| return -EIO; |
| } |
| #endif |
| data->tmp1075_dev = dev; |
| |
| uint16_t config_reg = 0; |
| |
| TMP1075_SET_ONE_SHOT_CONVERSION(config_reg, cfg->one_shot); |
| TMP1075_SET_CONVERSION_RATE(config_reg, cfg->cr); |
| TMP1075_SET_CONSECUTIVE_FAULT_MEASUREMENTS(config_reg, cfg->cf); |
| TMP1075_SET_ALERT_PIN_POLARITY(config_reg, cfg->alert_pol); |
| TMP1075_SET_ALERT_PIN_FUNCTION(config_reg, cfg->interrupt_mode); |
| TMP1075_SET_SHUTDOWN_MODE(config_reg, cfg->shutdown_mode); |
| |
| int rc = tmp1075_reg_write(dev->config, TMP1075_REG_CONFIG, config_reg); |
| |
| if (rc == 0) { |
| data->config_reg = config_reg; |
| } |
| return rc; |
| } |
| |
| #define TMP1075_INST(inst) \ |
| static struct tmp1075_data tmp1075_data_##inst; \ |
| static const struct tmp1075_config tmp1075_config_##inst = { \ |
| .cr = DT_INST_ENUM_IDX(inst, conversion_rate), \ |
| .cf = DT_INST_ENUM_IDX(inst, consecutive_fault_measurements), \ |
| .alert_pol = DT_INST_PROP(inst, alert_pin_active_high), \ |
| .interrupt_mode = DT_INST_PROP(inst, interrupt_mode), \ |
| .shutdown_mode = DT_INST_PROP(inst, shutdown_mode), \ |
| .bus = I2C_DT_SPEC_INST_GET(inst), \ |
| .alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, alert_gpios, {0}), \ |
| }; \ |
| SENSOR_DEVICE_DT_INST_DEFINE(inst, tmp1075_init, NULL, &tmp1075_data_##inst, \ |
| \ |
| &tmp1075_config_##inst, POST_KERNEL, \ |
| CONFIG_SENSOR_INIT_PRIORITY, &tmp1075_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(TMP1075_INST) |