Commit 5a9bc389 authored by Wealian Liao's avatar Wealian Liao Committed by Anas Nashif
Browse files

driver: gpio: nct38xx: Add NCT38XX gpio driver support



NCT38XX series, which are i2c-based chips, support a different number
of GPIO functionality. For NCT3807, it has 2 GPIO ports on the same i2c
device address. For NCT3808, it has 2 GPIO ports on different i2c
device addresses. This commit adds NCT38XX GPIO driver support &
provides the interrupt handler for the share alert pin.

The following is NCT3807 devicetree node example:
```
&i2c0_0 {
	nct3807_0: nct3807@70 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "nuvoton,nct38xx-gpio";
		reg = <0x70>;
		label = "NCT3807_0";

		gpio@0 {
			compatible = "nuvoton,nct38xx-gpio-port";
			reg = <0x0>;
			label = "NCT3807_0_GPIO0";
			gpio-controller;
			#gpio-cells = <2>;
			ngpios = <8>;
			pin_mask = <0xff>;
			pinmux_mask = <0xf7>;
		};

		gpio@1 {
			compatible = "nuvoton,nct38xx-gpio-port";
			reg = <0x1>;
			label = "NCT3807_0_GPIO1";
			gpio-controller;
			#gpio-cells = <2>;
			ngpios = <8>;
			pin_mask = <0xff>;
		};
	};
};
```

Signed-off-by: default avatarWealian Liao <WHLIAO@nuvoton.com>
parent d6920625
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -248,6 +248,7 @@
/drivers/gpio/                            @mnkp
/drivers/gpio/*b91*                       @yurvyn
/drivers/gpio/*lmp90xxx*                  @henrikbrixandersen
/drivers/gpio/*nct38xx*                   @MulinChao @WealianLiao @ChiHuaL
/drivers/gpio/*stm32*                     @erwango
/drivers/gpio/*eos_s3*                    @wtatarski @kowalewskijan @kgugala
/drivers/gpio/*rcar*                      @julien-massot
+4 −0
Original line number Diff line number Diff line
@@ -47,6 +47,10 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_PCA953X gpio_pca953x.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_FXL6408    gpio_fxl6408.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_ANDES_ATCGPIO100 gpio_andes_atcgpio100.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_NEORV32    gpio_neorv32.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX    gpio_nct38xx.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX    gpio_nct38xx_port.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX_INTERRUPT gpio_nct38xx_alert.c)


zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL      gpio_shell.c)

+2 −0
Original line number Diff line number Diff line
@@ -113,4 +113,6 @@ source "drivers/gpio/Kconfig.andes_atcgpio100"

source "drivers/gpio/Kconfig.neorv32"

source "drivers/gpio/Kconfig.nct38xx"

endif # GPIO
+41 −0
Original line number Diff line number Diff line
# NPCX GPIO driver configuration options

# Copyright (c) 2021 Nuvoton Technology Corporation.
# SPDX-License-Identifier: Apache-2.0

config GPIO_NCT38XX
	bool "NCT38XX I2C-based GPIO chip"
	depends on I2C
	help
	  Enable driver for NCT38XX I2C-based GPIO chip.

if GPIO_NCT38XX

config GPIO_NCT38XX_INIT_PRIORITY
	int "Init priority"
	default 30
	help
	  Device driver initialization priority. The priority should be lower
	  than I2C device.

config GPIO_NCT38XX_PORT_INIT_PRIORITY
	int "Init priority"
	default 40
	help
	  Device driver initialization priority. The priority should be lower
	  than I2C & GPIO_NCT38XX_INIT_PRIORITY device.

config GPIO_NCT38XX_INTERRUPT
	bool "Interrupt enable"
	help
	  Enable interrupt support in NCT38XX driver.

config GPIO_NCT38XX_ALERT_INIT_PRIORITY
	int "Init priority"
	default 40
	depends on GPIO_NCT38XX_INTERRUPT
	help
	  NCT38XX alert handler initialization priority. This initialization
	  must take place after the alert GPIO device is initialized.

endif # GPIO_NCT38XX
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2021 Nuvoton Technology Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT nuvoton_nct38xx_gpio

#include <device.h>
#include <drivers/gpio.h>
#include <drivers/gpio/gpio_nct38xx.h>
#include <kernel.h>
#include <sys/util_macro.h>

#include "gpio_nct38xx.h"

#include <logging/log.h>
LOG_MODULE_REGISTER(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);

/* Driver convenience defines */
#define DRV_CONFIG(dev) ((const struct gpio_nct38xx_config *)(dev)->config)
#define DRV_DATA(dev) ((struct gpio_nct38xx_data *)(dev)->data)

void nct38xx_gpio_alert_handler(const struct device *dev)
{
	const struct gpio_nct38xx_config *const config = DRV_CONFIG(dev);

	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;

	/* Disable all interrupt */
	if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
				    sizeof(alert_mask))) {
		return -EIO;
	}

	/* Enable vendor-defined alert for GPIO. */
	alert_mask |= BIT(NCT38XX_REG_ALERT_MASK_VENDOR_DEFINDED_ALERT);

	/* Clear alert */
	if (nct38xx_reg_burst_read(dev, NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) {
		return -EIO;
	}
	alert &= alert_mask;
	if (alert) {
		if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT, (uint8_t *)&alert,
					    sizeof(alert))) {
			return -EIO;
		}
	}

	if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
				    sizeof(alert_mask))) {
		return -EIO;
	}

	return 0;
}

static int nct38xx_gpio_init(const struct device *dev)
{
	const struct gpio_nct38xx_config *const config = DRV_CONFIG(dev);

	/* Check I2C is ready  */
	if (!device_is_ready(config->i2c_dev.bus)) {
		LOG_ERR("%s device not ready", config->i2c_dev.bus->name);
		return -ENODEV;
	}

	if (IS_ENABLED(CONFIG_GPIO_NCT38XX_INTERRUPT)) {
		nct38xx_init_interrupt(dev);
	}

	return 0;
}

#define GPIO_DEV_AND_COMMA(node_id) DEVICE_DT_GET(node_id),

#define GPIO_NCT38XX_DEVICE_INSTANCE(inst)                                                         \
	static const struct device *sub_gpio_dev_##inst[] = { DT_FOREACH_CHILD_STATUS_OKAY(        \
		DT_DRV_INST(inst), GPIO_DEV_AND_COMMA) };                                          \
	static const struct gpio_nct38xx_config gpio_nct38xx_cfg_##inst = {                        \
		.i2c_dev = I2C_DT_SPEC_INST_GET(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)
Loading