2021-11-08 11:02:01 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Nuvoton Technology Corporation.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT nuvoton_nct38xx_gpio
|
|
|
|
|
2022-05-06 16:25:46 +08:00
|
|
|
#include <zephyr/device.h>
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#include <zephyr/drivers/gpio/gpio_nct38xx.h>
|
2023-07-20 02:02:34 +08:00
|
|
|
#include <zephyr/drivers/mfd/nct38xx.h>
|
2022-05-06 16:25:46 +08:00
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/sys/util_macro.h>
|
2021-11-08 11:02:01 +08:00
|
|
|
|
|
|
|
#include "gpio_nct38xx.h"
|
|
|
|
|
2022-05-06 16:25:46 +08:00
|
|
|
#include <zephyr/logging/log.h>
|
2021-11-08 11:02:01 +08:00
|
|
|
LOG_MODULE_REGISTER(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);
|
|
|
|
|
2023-07-20 02:02:34 +08:00
|
|
|
/* 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;
|
2023-08-12 03:50:26 +08:00
|
|
|
/* I2C device for the MFD parent */
|
|
|
|
const struct i2c_dt_spec *i2c_dev;
|
2023-07-20 02:02:34 +08:00
|
|
|
};
|
|
|
|
|
2021-11-08 11:02:01 +08:00
|
|
|
void nct38xx_gpio_alert_handler(const struct device *dev)
|
|
|
|
{
|
2022-01-21 15:38:43 +08:00
|
|
|
const struct gpio_nct38xx_config *const config = dev->config;
|
2021-11-08 11:02:01 +08:00
|
|
|
|
|
|
|
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;
|
2023-07-20 02:02:34 +08:00
|
|
|
int ret = 0;
|
|
|
|
struct gpio_nct38xx_data *data = dev->data;
|
|
|
|
|
|
|
|
k_sem_take(data->lock, K_FOREVER);
|
2021-11-08 11:02:01 +08:00
|
|
|
|
|
|
|
/* Disable all interrupt */
|
2023-08-12 03:50:26 +08:00
|
|
|
if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
|
|
|
|
sizeof(alert_mask))) {
|
2023-07-20 02:02:34 +08:00
|
|
|
ret = -EIO;
|
|
|
|
goto unlock;
|
2021-11-08 11:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable vendor-defined alert for GPIO. */
|
|
|
|
alert_mask |= BIT(NCT38XX_REG_ALERT_MASK_VENDOR_DEFINDED_ALERT);
|
|
|
|
|
|
|
|
/* Clear alert */
|
2023-08-12 03:50:26 +08:00
|
|
|
if (i2c_burst_read_dt(data->i2c_dev, NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) {
|
2023-07-20 02:02:34 +08:00
|
|
|
ret = -EIO;
|
|
|
|
goto unlock;
|
2021-11-08 11:02:01 +08:00
|
|
|
}
|
|
|
|
alert &= alert_mask;
|
|
|
|
if (alert) {
|
2023-08-12 03:50:26 +08:00
|
|
|
if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT, (uint8_t *)&alert,
|
|
|
|
sizeof(alert))) {
|
2023-07-20 02:02:34 +08:00
|
|
|
ret = -EIO;
|
|
|
|
goto unlock;
|
2021-11-08 11:02:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-12 03:50:26 +08:00
|
|
|
if (i2c_burst_write_dt(data->i2c_dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
|
|
|
|
sizeof(alert_mask))) {
|
2023-07-20 02:02:34 +08:00
|
|
|
ret = -EIO;
|
|
|
|
goto unlock;
|
2021-11-08 11:02:01 +08:00
|
|
|
}
|
|
|
|
|
2023-07-20 02:02:34 +08:00
|
|
|
unlock:
|
|
|
|
k_sem_give(data->lock);
|
|
|
|
return ret;
|
2021-11-08 11:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nct38xx_gpio_init(const struct device *dev)
|
|
|
|
{
|
2022-01-21 15:38:43 +08:00
|
|
|
const struct gpio_nct38xx_config *const config = dev->config;
|
2023-07-20 02:02:34 +08:00
|
|
|
struct gpio_nct38xx_data *data = dev->data;
|
2021-11-08 11:02:01 +08:00
|
|
|
|
2023-07-20 02:02:34 +08:00
|
|
|
/* Verify multi-function parent is ready */
|
|
|
|
if (!device_is_ready(config->mfd)) {
|
|
|
|
LOG_ERR("%s device not ready", config->mfd->name);
|
2021-11-08 11:02:01 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2023-07-20 02:02:34 +08:00
|
|
|
data->lock = mfd_nct38xx_get_lock_reference(config->mfd);
|
2023-08-12 03:50:26 +08:00
|
|
|
data->i2c_dev = mfd_nct38xx_get_i2c_dt_spec(config->mfd);
|
2023-07-20 02:02:34 +08:00
|
|
|
|
2023-08-12 04:53:14 +08:00
|
|
|
if (IS_ENABLED(CONFIG_GPIO_NCT38XX_ALERT)) {
|
2021-11-08 11:02:01 +08:00
|
|
|
nct38xx_init_interrupt(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GPIO_NCT38XX_DEVICE_INSTANCE(inst) \
|
2022-07-06 20:04:09 +08:00
|
|
|
static const struct device *sub_gpio_dev_##inst[] = { \
|
|
|
|
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, DEVICE_DT_GET, (,)) \
|
|
|
|
}; \
|
2021-11-08 11:02:01 +08:00
|
|
|
static const struct gpio_nct38xx_config gpio_nct38xx_cfg_##inst = { \
|
2023-07-20 02:02:34 +08:00
|
|
|
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
2021-11-08 11:02:01 +08:00
|
|
|
.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)
|
2023-07-20 02:02:34 +08:00
|
|
|
|
|
|
|
/* The nct38xx MFD parent must be initialized before this driver */
|
|
|
|
BUILD_ASSERT(CONFIG_GPIO_NCT38XX_INIT_PRIORITY > CONFIG_MFD_INIT_PRIORITY);
|