/* * Copyright (c) 2020 arithmetics.io * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ti_fdc2x1x #include #include #include #include #include #include "fdc2x1x.h" #include #include LOG_MODULE_DECLARE(FDC2X1X, CONFIG_SENSOR_LOG_LEVEL); static void fdc2x1x_thread_cb(const struct device *dev) { struct fdc2x1x_data *drv_data = dev->data; uint16_t status; #ifdef CONFIG_PM_DEVICE enum pm_device_state state; /* INTB asserts after exiting shutdown mode. Drop this interrupt */ (void)pm_device_state_get(dev, &state); if (state == PM_DEVICE_STATE_OFF) { return; } #endif /* Clear the status */ if (fdc2x1x_get_status(dev, &status) < 0) { LOG_ERR("Unable to get status."); return; } k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER); if ((drv_data->drdy_handler != NULL) && FDC2X1X_STATUS_DRDY(status)) { drv_data->drdy_handler(dev, drv_data->drdy_trigger); } k_mutex_unlock(&drv_data->trigger_mutex); } static void fdc2x1x_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct fdc2x1x_data *drv_data = CONTAINER_OF(cb, struct fdc2x1x_data, gpio_cb); #ifdef CONFIG_FDC2X1X_TRIGGER_OWN_THREAD k_sem_give(&drv_data->gpio_sem); #elif CONFIG_FDC2X1X_TRIGGER_GLOBAL_THREAD k_work_submit(&drv_data->work); #endif } #ifdef CONFIG_FDC2X1X_TRIGGER_OWN_THREAD static void fdc2x1x_thread(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); struct fdc2x1x_data *drv_data = p1; while (true) { k_sem_take(&drv_data->gpio_sem, K_FOREVER); fdc2x1x_thread_cb(drv_data->dev); } } #elif CONFIG_FDC2X1X_TRIGGER_GLOBAL_THREAD static void fdc2x1x_work_cb(struct k_work *work) { struct fdc2x1x_data *drv_data = CONTAINER_OF(work, struct fdc2x1x_data, work); fdc2x1x_thread_cb(drv_data->dev); } #endif int fdc2x1x_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { struct fdc2x1x_data *drv_data = dev->data; const struct fdc2x1x_config *cfg = dev->config; uint16_t status, int_mask, int_en; int ret; gpio_pin_interrupt_configure_dt(&cfg->intb_gpio, GPIO_INT_DISABLE); switch (trig->type) { case SENSOR_TRIG_DATA_READY: k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER); drv_data->drdy_handler = handler; drv_data->drdy_trigger = trig; k_mutex_unlock(&drv_data->trigger_mutex); int_mask = FDC2X1X_ERROR_CONFIG_DRDY_2INT_MSK; break; default: LOG_ERR("Unsupported sensor trigger"); ret = -ENOTSUP; goto out; } if (handler) { int_en = int_mask; drv_data->int_config |= int_mask; } else { int_en = 0U; } ret = fdc2x1x_reg_write_mask(dev, FDC2X1X_ERROR_CONFIG, int_mask, int_en); /* Clear INTB pin by reading STATUS register */ fdc2x1x_get_status(dev, &status); out: gpio_pin_interrupt_configure_dt(&cfg->intb_gpio, GPIO_INT_EDGE_TO_ACTIVE); return ret; } int fdc2x1x_init_interrupt(const struct device *dev) { struct fdc2x1x_data *drv_data = dev->data; const struct fdc2x1x_config *cfg = dev->config; int ret; k_mutex_init(&drv_data->trigger_mutex); if (!gpio_is_ready_dt(&cfg->intb_gpio)) { LOG_ERR("%s: intb_gpio device not ready", cfg->intb_gpio.port->name); return -ENODEV; } ret = fdc2x1x_set_interrupt_pin(dev, true); if (ret) { return ret; } gpio_pin_configure_dt(&cfg->intb_gpio, GPIO_INPUT); gpio_init_callback(&drv_data->gpio_cb, fdc2x1x_gpio_callback, BIT(cfg->intb_gpio.pin)); if (gpio_add_callback(cfg->intb_gpio.port, &drv_data->gpio_cb) < 0) { LOG_ERR("Failed to set gpio callback!"); return -EIO; } drv_data->dev = dev; #ifdef CONFIG_FDC2X1X_TRIGGER_OWN_THREAD k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_FDC2X1X_THREAD_STACK_SIZE, fdc2x1x_thread, drv_data, 0, NULL, K_PRIO_COOP(CONFIG_FDC2X1X_THREAD_PRIORITY), 0, K_NO_WAIT); #elif CONFIG_FDC2X1X_TRIGGER_GLOBAL_THREAD drv_data->work.handler = fdc2x1x_work_cb; #endif return 0; }