/* * Copyright 2023 Daniel DeGrasse * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_DECLARE(tcn75a, CONFIG_SENSOR_LOG_LEVEL); #include "tcn75a.h" int tcn75a_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { const struct tcn75a_config *config = dev->config; struct tcn75a_data *data = dev->data; int ret; if (trig->type != SENSOR_TRIG_THRESHOLD) { return -ENOTSUP; } if ((trig->chan != SENSOR_CHAN_ALL) && (trig->chan != SENSOR_CHAN_AMBIENT_TEMP)) { return -ENOTSUP; } data->sensor_cb = handler; data->sensor_trig = trig; /* TCN75A starts in comparator mode by default, switch it to * use interrupt mode. */ ret = i2c_reg_update_byte_dt(&config->i2c_spec, TCN75A_CONFIG_REG, TCN75A_CONFIG_INT_EN, TCN75A_CONFIG_INT_EN); if (ret < 0) { return ret; } ret = gpio_pin_interrupt_configure_dt(&config->alert_gpios, GPIO_INT_EDGE_TO_ACTIVE); if (ret < 0) { return ret; } return ret; } int tcn75a_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct tcn75a_config *config = dev->config; uint8_t tx_buf[3]; if ((chan != SENSOR_CHAN_AMBIENT_TEMP) && (chan != SENSOR_CHAN_ALL)) { return -ENOTSUP; } switch (attr) { case SENSOR_ATTR_LOWER_THRESH: tx_buf[0] = TCN75A_THYST_REG; break; case SENSOR_ATTR_UPPER_THRESH: tx_buf[0] = TCN75A_TSET_REG; break; default: return -ENOTSUP; } /* Convert sensor val to fixed point */ tx_buf[1] = (uint8_t)val->val1; tx_buf[2] = TCN75A_SENSOR_TO_FIXED_PT(val->val2); LOG_DBG("Writing 0x%02X to limit reg %s", *(uint16_t *)(tx_buf + 1), tx_buf[0] == TCN75A_THYST_REG ? "THYST" : "TSET"); return i2c_write_dt(&config->i2c_spec, tx_buf, 3); } int tcn75a_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { const struct tcn75a_config *config = dev->config; uint8_t config_reg; uint8_t rx_buf[2]; uint16_t limit, temp_lsb; int ret; if ((chan != SENSOR_CHAN_AMBIENT_TEMP) && (chan != SENSOR_CHAN_ALL)) { return -ENOTSUP; } switch (attr) { case SENSOR_ATTR_LOWER_THRESH: config_reg = TCN75A_THYST_REG; break; case SENSOR_ATTR_UPPER_THRESH: config_reg = TCN75A_TSET_REG; break; default: return -ENOTSUP; } ret = i2c_write_read_dt(&config->i2c_spec, &config_reg, 1, rx_buf, 2); if (ret < 0) { return ret; } limit = sys_get_be16(rx_buf); LOG_DBG("Read 0x%02X from %s", limit, config_reg == TCN75A_THYST_REG ? "THYST" : "TSET"); /* Convert fixed point to sensor value */ val->val1 = limit >> TCN75A_TEMP_MSB_POS; temp_lsb = (limit & TCN75A_TEMP_LSB_MASK); val->val2 = TCN75A_FIXED_PT_TO_SENSOR(temp_lsb); return ret; } static void tcn75a_handle_int(const struct device *dev) { struct tcn75a_data *data = dev->data; /* Note that once the temperature rises * above T_SET, the sensor will not trigger another interrupt until * it falls below T_HYST (or vice versa for falling below T_HYST). * * Reading from any register will de-assert the interrupt. */ if (data->sensor_cb) { data->sensor_cb(dev, data->sensor_trig); } } static void tcn75a_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pin_mask) { struct tcn75a_data *data = CONTAINER_OF(cb, struct tcn75a_data, gpio_cb); const struct tcn75a_config *config = data->dev->config; if ((pin_mask & BIT(config->alert_gpios.pin)) == 0U) { return; } #if defined(CONFIG_TCN75A_TRIGGER_OWN_THREAD) k_sem_give(&data->trig_sem); #elif defined(CONFIG_TCN75A_TRIGGER_GLOBAL_THREAD) k_work_submit(&data->work); #endif } #ifdef CONFIG_TCN75A_TRIGGER_OWN_THREAD static void tcn75a_thread_main(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); struct tcn75a_data *data = p1; while (true) { k_sem_take(&data->trig_sem, K_FOREVER); tcn75a_handle_int(data->dev); } } #endif #ifdef CONFIG_TCN75A_TRIGGER_GLOBAL_THREAD static void tcn75a_work_handler(struct k_work *work) { struct tcn75a_data *data = CONTAINER_OF(work, struct tcn75a_data, work); tcn75a_handle_int(data->dev); } #endif int tcn75a_trigger_init(const struct device *dev) { const struct tcn75a_config *config = dev->config; struct tcn75a_data *data = dev->data; int ret; /* Save config pointer */ data->dev = dev; if (!gpio_is_ready_dt(&config->alert_gpios)) { LOG_ERR("alert GPIO device is not ready"); return -ENODEV; } ret = gpio_pin_configure_dt(&config->alert_gpios, GPIO_INPUT); if (ret < 0) { return ret; } gpio_init_callback(&data->gpio_cb, tcn75a_gpio_callback, BIT(config->alert_gpios.pin)); ret = gpio_add_callback(config->alert_gpios.port, &data->gpio_cb); #if defined(CONFIG_TCN75A_TRIGGER_OWN_THREAD) k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT); k_thread_create(&data->thread, data->thread_stack, CONFIG_TCN75A_THREAD_STACK_SIZE, tcn75a_thread_main, data, NULL, NULL, K_PRIO_COOP(CONFIG_TCN75A_THREAD_PRIORITY), 0, K_NO_WAIT); #elif defined(CONFIG_TCN75A_TRIGGER_GLOBAL_THREAD) data->work.handler = tcn75a_work_handler; #endif return ret; }