/* * Copyright (c) 2021 Nuvoton Technology Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_nct38xx_gpio_alert #include #include #include #include #include #include "gpio_nct38xx.h" #include LOG_MODULE_DECLARE(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL); /* Driver config */ struct nct38xx_alert_config { /* Alert GPIO pin */ const struct gpio_dt_spec irq_gpio; /* NCT38XX devices which share the same alert pin */ const struct device **nct38xx_dev; /* Number of NCT38XX devices on the alert pin */ uint32_t nct38xx_num; }; /* Driver data */ struct nct38xx_alert_data { /* Alert handler device */ const struct device *alert_dev; /* Alert pin callback */ struct gpio_callback gpio_cb; /* Alert worker */ struct k_work alert_worker; }; static void nct38xx_alert_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { ARG_UNUSED(pins); struct nct38xx_alert_data *data = CONTAINER_OF(cb, struct nct38xx_alert_data, gpio_cb); k_work_submit(&data->alert_worker); } static void nct38xx_alert_worker(struct k_work *work) { struct nct38xx_alert_data *const data = CONTAINER_OF(work, struct nct38xx_alert_data, alert_worker); const struct nct38xx_alert_config *const config = data->alert_dev->config; uint16_t alert, mask; do { /* NCT38XX device handler */ for (int i = 0; i < config->nct38xx_num; i++) { /* Clear alert */ if (nct38xx_reg_burst_read(config->nct38xx_dev[i], NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) { LOG_ERR("i2c access failed"); return; } if (nct38xx_reg_burst_read(config->nct38xx_dev[i], NCT38XX_REG_ALERT_MASK, (uint8_t *)&mask, sizeof(mask))) { LOG_ERR("i2c access failed"); return; } alert &= mask; if (alert) { if (nct38xx_reg_burst_write(config->nct38xx_dev[i], NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) { LOG_ERR("i2c access failed"); return; } } if (alert & BIT(NCT38XX_REG_ALERT_VENDOR_DEFINDED_ALERT)) { nct38xx_gpio_alert_handler(config->nct38xx_dev[i]); } } /* While the interrupt signal is still active; we have more work to do. */ } while (gpio_pin_get_dt(&config->irq_gpio)); } static int nct38xx_alert_init(const struct device *dev) { const struct nct38xx_alert_config *const config = dev->config; struct nct38xx_alert_data *const data = dev->data; int ret; /* Check NCT38XX devices are all ready. */ for (int i = 0; i < config->nct38xx_num; i++) { if (!device_is_ready(config->nct38xx_dev[i])) { LOG_ERR("%s device not ready", config->nct38xx_dev[i]->name); return -ENODEV; } } /* Set the alert pin for handling the interrupt */ k_work_init(&data->alert_worker, nct38xx_alert_worker); if (!device_is_ready(config->irq_gpio.port)) { LOG_ERR("%s device not ready", config->irq_gpio.port->name); return -ENODEV; } gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); gpio_init_callback(&data->gpio_cb, nct38xx_alert_callback, BIT(config->irq_gpio.pin)); ret = gpio_add_callback(config->irq_gpio.port, &data->gpio_cb); if (ret < 0) { return ret; } gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); return 0; } /* NCT38XX alert driver must be initialized after NCT38XX GPIO driver */ BUILD_ASSERT(CONFIG_GPIO_NCT38XX_ALERT_INIT_PRIORITY > CONFIG_GPIO_NCT38XX_INIT_PRIORITY); #define NCT38XX_DEV_AND_COMMA(node_id, prop, idx) \ DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), #define NCT38XX_ALERT_DEVICE_INSTANCE(inst) \ const struct device *nct38xx_dev_##inst[] = { DT_INST_FOREACH_PROP_ELEM( \ inst, nct38xx_dev, NCT38XX_DEV_AND_COMMA) }; \ static const struct nct38xx_alert_config nct38xx_alert_cfg_##inst = { \ .irq_gpio = GPIO_DT_SPEC_INST_GET(inst, irq_gpios), \ .nct38xx_dev = &nct38xx_dev_##inst[0], \ .nct38xx_num = DT_INST_PROP_LEN(inst, nct38xx_dev), \ }; \ static struct nct38xx_alert_data nct38xx_alert_data_##inst = { \ .alert_dev = DEVICE_DT_INST_GET(inst), \ }; \ DEVICE_DT_INST_DEFINE(inst, nct38xx_alert_init, NULL, &nct38xx_alert_data_##inst, \ &nct38xx_alert_cfg_##inst, POST_KERNEL, \ CONFIG_GPIO_NCT38XX_ALERT_INIT_PRIORITY, NULL); DT_INST_FOREACH_STATUS_OKAY(NCT38XX_ALERT_DEVICE_INSTANCE)