/* * Copyright (c) 2021 Nuvoton Technology Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_nct38xx_gpio #include #include #include #include #include #include #include "gpio_nct38xx.h" #include LOG_MODULE_REGISTER(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL); /* Driver config */ struct gpio_nct38xx_config { /* Multi-function device, parent to the NCT38xx GPIO controller */ const struct device *mfd; /* GPIO ports */ const struct device **sub_gpio_dev; uint8_t sub_gpio_port_num; /* Alert handler */ const struct device *alert_dev; }; /* Driver data */ struct gpio_nct38xx_data { /* NCT38XX device */ const struct device *dev; /* lock NCT38xx register access */ struct k_sem *lock; /* I2C device for the MFD parent */ const struct i2c_dt_spec *i2c_dev; }; void nct38xx_gpio_alert_handler(const struct device *dev) { const struct gpio_nct38xx_config *const config = dev->config; for (int i = 0; i < config->sub_gpio_port_num; i++) { gpio_nct38xx_dispatch_port_isr(config->sub_gpio_dev[i]); } } static int nct38xx_init_interrupt(const struct device *dev) { uint16_t alert, alert_mask = 0; int ret = 0; struct gpio_nct38xx_data *data = dev->data; k_sem_take(data->lock, K_FOREVER); /* Disable all interrupt */ if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask, sizeof(alert_mask))) { ret = -EIO; goto unlock; } /* Enable vendor-defined alert for GPIO. */ alert_mask |= BIT(NCT38XX_REG_ALERT_MASK_VENDOR_DEFINDED_ALERT); /* Clear alert */ if (i2c_burst_read_dt(data->i2c_dev, NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) { ret = -EIO; goto unlock; } alert &= alert_mask; if (alert) { if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) { ret = -EIO; goto unlock; } } if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask, sizeof(alert_mask))) { ret = -EIO; goto unlock; } unlock: k_sem_give(data->lock); return ret; } static int nct38xx_gpio_init(const struct device *dev) { const struct gpio_nct38xx_config *const config = dev->config; struct gpio_nct38xx_data *data = dev->data; /* Verify multi-function parent is ready */ if (!device_is_ready(config->mfd)) { LOG_ERR("%s device not ready", config->mfd->name); return -ENODEV; } data->lock = mfd_nct38xx_get_lock_reference(config->mfd); data->i2c_dev = mfd_nct38xx_get_i2c_dt_spec(config->mfd); if (IS_ENABLED(CONFIG_GPIO_NCT38XX_ALERT)) { nct38xx_init_interrupt(dev); } return 0; } #define GPIO_NCT38XX_DEVICE_INSTANCE(inst) \ static const struct device *sub_gpio_dev_##inst[] = { \ DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, DEVICE_DT_GET, (,)) \ }; \ static const struct gpio_nct38xx_config gpio_nct38xx_cfg_##inst = { \ .mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ .sub_gpio_dev = sub_gpio_dev_##inst, \ .sub_gpio_port_num = ARRAY_SIZE(sub_gpio_dev_##inst), \ }; \ static struct gpio_nct38xx_data gpio_nct38xx_data_##inst = { \ .dev = DEVICE_DT_INST_GET(inst), \ }; \ DEVICE_DT_INST_DEFINE(inst, nct38xx_gpio_init, NULL, &gpio_nct38xx_data_##inst, \ &gpio_nct38xx_cfg_##inst, POST_KERNEL, \ CONFIG_GPIO_NCT38XX_INIT_PRIORITY, NULL); DT_INST_FOREACH_STATUS_OKAY(GPIO_NCT38XX_DEVICE_INSTANCE) /* The nct38xx MFD parent must be initialized before this driver */ BUILD_ASSERT(CONFIG_GPIO_NCT38XX_INIT_PRIORITY > CONFIG_MFD_INIT_PRIORITY);