blob: 12635eb00c22f65640715c6b25d9570c4ba3716b [file] [log] [blame]
/*
* Copyright 2023 Daniel DeGrasse <daniel@degrasse.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
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;
}