Browse Source
Add I2C-based GPIO device driver. Supports 16-port GPIO divided into 3 groups. Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>pull/82508/head
6 changed files with 499 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||||||
|
# Copyright (c) 2024 ITE Corporation. All Rights Reserved. |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
config GPIO_ITE_IT8801 |
||||||
|
bool "ITE IT8801 GPIO device driver" |
||||||
|
default y |
||||||
|
depends on DT_HAS_ITE_IT8801_GPIO_ENABLED |
||||||
|
select I2C |
||||||
|
select MFD |
||||||
|
help |
||||||
|
Enable driver for ITE IT8801 I2C-based GPIO. |
@ -0,0 +1,462 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 ITE Corporation. All Rights Reserved. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ite_it8801_gpio |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/gpio.h> |
||||||
|
#include <zephyr/drivers/gpio/gpio_utils.h> |
||||||
|
#include <zephyr/drivers/i2c.h> |
||||||
|
#include <zephyr/drivers/mfd/mfd_ite_it8801.h> |
||||||
|
#include <zephyr/spinlock.h> |
||||||
|
|
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
LOG_MODULE_REGISTER(gpio_ite_it8801, CONFIG_GPIO_LOG_LEVEL); |
||||||
|
|
||||||
|
struct gpio_it8801_config { |
||||||
|
/* gpio_driver_config needs to be first */ |
||||||
|
struct gpio_driver_config common; |
||||||
|
/* IT8801 controller dev */ |
||||||
|
const struct device *mfd; |
||||||
|
/* I2C device for the MFD parent */ |
||||||
|
const struct i2c_dt_spec i2c_dev; |
||||||
|
/* GPIO input pin status register */ |
||||||
|
uint8_t reg_ipsr; |
||||||
|
/* GPIO set output value register */ |
||||||
|
uint8_t reg_sovr; |
||||||
|
/* GPIO control register */ |
||||||
|
uint8_t reg_gpcr; |
||||||
|
/* GPIO interrupt status register */ |
||||||
|
uint8_t reg_gpisr; |
||||||
|
/* GPIO interrupt enable register */ |
||||||
|
uint8_t reg_gpier; |
||||||
|
uint8_t pin_mask; |
||||||
|
}; |
||||||
|
|
||||||
|
struct gpio_it8801_data { |
||||||
|
struct gpio_driver_data common; |
||||||
|
struct it8801_mfd_callback it8801_gpio_callback; |
||||||
|
sys_slist_t callbacks; |
||||||
|
}; |
||||||
|
|
||||||
|
static int ioex_check_is_not_valid(const struct device *dev, gpio_pin_t pin) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
|
||||||
|
if (BIT(pin) & ~(config->pin_mask)) { |
||||||
|
LOG_ERR("GPIO port%d-%d is not support", config->reg_ipsr, pin); |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
uint8_t reg_gpcr = config->reg_gpcr + pin; |
||||||
|
uint8_t mask = BIT(pin); |
||||||
|
uint8_t new_value, control; |
||||||
|
|
||||||
|
/* Don't support "open source" mode */ |
||||||
|
if (((flags & GPIO_SINGLE_ENDED) != 0) && ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
if (ioex_check_is_not_valid(dev, pin)) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, reg_gpcr, &control); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read control value (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (flags == GPIO_DISCONNECTED) { |
||||||
|
control &= ~(IT8801_GPIODIR | IT8801_GPIOPDE | IT8801_GPIOPUE); |
||||||
|
|
||||||
|
goto write_and_return; |
||||||
|
} |
||||||
|
|
||||||
|
/* If output, set level before changing type to an output. */ |
||||||
|
if (flags & GPIO_OUTPUT) { |
||||||
|
if (flags & GPIO_OUTPUT_INIT_HIGH) { |
||||||
|
new_value = mask; |
||||||
|
} else if (flags & GPIO_OUTPUT_INIT_LOW) { |
||||||
|
new_value = 0; |
||||||
|
} else { |
||||||
|
new_value = 0; |
||||||
|
} |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_sovr, mask, new_value); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to set output value (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
/* Set output */ |
||||||
|
control |= IT8801_GPIODIR; |
||||||
|
/* Select open drain 0:push-pull 1:open-drain */ |
||||||
|
if (flags & GPIO_OPEN_DRAIN) { |
||||||
|
control |= IT8801_GPIOIOT_OD; |
||||||
|
} else { |
||||||
|
control &= ~IT8801_GPIOIOT_OD; |
||||||
|
} |
||||||
|
} else { |
||||||
|
/* Set input */ |
||||||
|
control &= ~IT8801_GPIODIR; |
||||||
|
} |
||||||
|
|
||||||
|
/* Handle pullup / pulldown */ |
||||||
|
if (flags & GPIO_PULL_UP) { |
||||||
|
control = (control | IT8801_GPIOPUE) & ~IT8801_GPIOPDE; |
||||||
|
} else if (flags & GPIO_PULL_DOWN) { |
||||||
|
control = (control | IT8801_GPIOPDE) & ~IT8801_GPIOPUE; |
||||||
|
} else { |
||||||
|
/* No pull up/down */ |
||||||
|
control &= ~(IT8801_GPIOPUE | IT8801_GPIOPDE); |
||||||
|
} |
||||||
|
|
||||||
|
write_and_return: |
||||||
|
/* Set GPIO control */ |
||||||
|
ret = i2c_reg_write_byte_dt(&config->i2c_dev, reg_gpcr, control); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to set control value (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef CONFIG_GPIO_GET_CONFIG |
||||||
|
static int gpio_it8801_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
gpio_flags_t flags = 0; |
||||||
|
int ret; |
||||||
|
uint8_t reg_gpcr = config->reg_gpcr + pin; |
||||||
|
uint8_t mask = BIT(pin); |
||||||
|
uint8_t control, value; |
||||||
|
|
||||||
|
if (ioex_check_is_not_valid(dev, pin)) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, reg_gpcr, &control); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read control value (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Get GPIO direction */ |
||||||
|
if (control & IT8801_GPIODIR) { |
||||||
|
flags |= GPIO_OUTPUT; |
||||||
|
|
||||||
|
/* Get GPIO type, 0:push-pull 1:open-drain */ |
||||||
|
if (control & IT8801_GPIOIOT_OD) { |
||||||
|
flags |= GPIO_OPEN_DRAIN; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_ipsr, &value); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read pin status (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Get GPIO output level */ |
||||||
|
if (value & mask) { |
||||||
|
flags |= GPIO_OUTPUT_HIGH; |
||||||
|
} else { |
||||||
|
flags |= GPIO_OUTPUT_LOW; |
||||||
|
} |
||||||
|
} else { |
||||||
|
flags |= GPIO_INPUT; |
||||||
|
} |
||||||
|
|
||||||
|
/* pullup / pulldown */ |
||||||
|
if (control & IT8801_GPIOPUE) { |
||||||
|
flags |= GPIO_PULL_UP; |
||||||
|
} else if (control & IT8801_GPIOPDE) { |
||||||
|
flags |= GPIO_PULL_DOWN; |
||||||
|
} |
||||||
|
|
||||||
|
*out_flags = flags; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
static int gpio_it8801_port_get_raw(const struct device *dev, gpio_port_value_t *value) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
uint8_t val; |
||||||
|
|
||||||
|
/* Get raw bits of GPIO mirror register */ |
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_ipsr, &val); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to get port mask (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
*value = val; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, |
||||||
|
gpio_port_value_t value) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_sovr, mask, value); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to set port mask (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
|
||||||
|
/* Set raw bits of GPIO data register */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_sovr, pins, pins); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to set bits raw (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
|
||||||
|
/* Clear raw bits of GPIO data register */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_sovr, pins, 0); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to clear bits raw (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
int ret; |
||||||
|
uint8_t val, new_val; |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_sovr, &val); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
/* Toggle raw bits of GPIO data register */ |
||||||
|
new_val = val ^ pins; |
||||||
|
if (new_val != val) { |
||||||
|
ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_sovr, new_val); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to write toggle value (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_manage_callback(const struct device *dev, struct gpio_callback *callback, |
||||||
|
bool set) |
||||||
|
{ |
||||||
|
struct gpio_it8801_data *data = dev->data; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = gpio_manage_callback(&data->callbacks, callback, set); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static void it8801_gpio_alert_handler(const struct device *dev) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
struct gpio_it8801_data *data = dev->data; |
||||||
|
int ret; |
||||||
|
uint8_t isr_val, ier_val; |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_gpisr, &isr_val); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read GPIO interrupt status (ret %d)", ret); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_gpier, &ier_val); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read GPIO interrupt pin set (ret %d)", ret); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (isr_val & ier_val) { |
||||||
|
/* Clear pending interrupt */ |
||||||
|
ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_gpisr, isr_val); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to clear GPIO interrupt (ret %d)", ret); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gpio_fire_callbacks(&data->callbacks, dev, isr_val); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_it8801_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, |
||||||
|
enum gpio_int_mode mode, enum gpio_int_trig trig) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
struct gpio_it8801_data *data = dev->data; |
||||||
|
int ret; |
||||||
|
uint8_t reg_gpcr = config->reg_gpcr + pin; |
||||||
|
uint8_t control; |
||||||
|
uint8_t mask = BIT(pin); |
||||||
|
|
||||||
|
if (ioex_check_is_not_valid(dev, pin)) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
/* Disable irq before configuring it */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_gpier, mask, 0); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to disable irq (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (mode == GPIO_INT_MODE_DISABLED) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Set input pin */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, reg_gpcr, IT8801_GPIODIR, 0); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to set input pin (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Clear trigger type */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, reg_gpcr, GENMASK(4, 3), 0); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to clear trigger type (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&config->i2c_dev, reg_gpcr, &control); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to read gpio control (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (mode == GPIO_INT_MODE_EDGE) { |
||||||
|
/* Set edge trigger */ |
||||||
|
if ((trig & GPIO_INT_TRIG_BOTH) == GPIO_INT_TRIG_BOTH) { |
||||||
|
control |= IT8801_GPIOIOT_INT_FALL | IT8801_GPIOIOT_INT_RISE; |
||||||
|
} else if (trig & GPIO_INT_TRIG_LOW) { |
||||||
|
control |= IT8801_GPIOIOT_INT_FALL; |
||||||
|
} else if (trig & GPIO_INT_TRIG_HIGH) { |
||||||
|
control |= IT8801_GPIOIOT_INT_RISE; |
||||||
|
} else { |
||||||
|
LOG_ERR("Invalid interrupt trigger type %d", trig); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
} else if (mode == GPIO_INT_MODE_LEVEL) { |
||||||
|
/* Set level trigger */ |
||||||
|
if (trig & GPIO_INT_TRIG_LOW) { |
||||||
|
control &= ~IT8801_GPIOPOL; |
||||||
|
} else { |
||||||
|
control |= IT8801_GPIOPOL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Set control value */ |
||||||
|
ret = i2c_reg_write_byte_dt(&config->i2c_dev, reg_gpcr, control); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to write trigger state (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Clear pending interrupt */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_gpisr, mask, mask); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to clear pending interrupt (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Enable GPIO interrupt */ |
||||||
|
ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_gpier, mask, mask); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to enable interrupt (ret %d)", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* Gather GPIO interrupt enable */ |
||||||
|
ret = i2c_reg_write_byte_dt(&config->i2c_dev, IT8801_REG_GIECR, IT8801_REG_MASK_GGPIOIE); |
||||||
|
|
||||||
|
/* Register the interrupt of IT8801 MFD callback function */ |
||||||
|
data->it8801_gpio_callback.cb = it8801_gpio_alert_handler; |
||||||
|
data->it8801_gpio_callback.dev = dev; |
||||||
|
mfd_it8801_register_interrupt_callback(config->mfd, &data->it8801_gpio_callback); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct gpio_driver_api gpio_it8801_driver_api = { |
||||||
|
.pin_configure = gpio_it8801_configure, |
||||||
|
#ifdef CONFIG_GPIO_GET_CONFIG |
||||||
|
.pin_get_config = gpio_it8801_get_config, |
||||||
|
#endif |
||||||
|
.port_get_raw = gpio_it8801_port_get_raw, |
||||||
|
.port_set_masked_raw = gpio_it8801_port_set_masked_raw, |
||||||
|
.port_set_bits_raw = gpio_it8801_port_set_bits_raw, |
||||||
|
.port_clear_bits_raw = gpio_it8801_port_clear_bits_raw, |
||||||
|
.port_toggle_bits = gpio_it8801_port_toggle_bits, |
||||||
|
.pin_interrupt_configure = gpio_it8801_pin_interrupt_configure, |
||||||
|
.manage_callback = gpio_it8801_manage_callback, |
||||||
|
}; |
||||||
|
|
||||||
|
static int gpio_it8801_init(const struct device *dev) |
||||||
|
{ |
||||||
|
const struct gpio_it8801_config *config = dev->config; |
||||||
|
|
||||||
|
/* Verify multi-function parent is ready */ |
||||||
|
if (!device_is_ready(config->mfd)) { |
||||||
|
LOG_ERR("(gpio)%s is not ready", config->mfd->name); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
#define GPIO_IT8801_DEVICE_INST(inst) \ |
||||||
|
static struct gpio_it8801_data gpio_it8801_data_##inst; \ |
||||||
|
static const struct gpio_it8801_config gpio_it8801_cfg_##inst = { \ |
||||||
|
.common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst)}, \ |
||||||
|
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ |
||||||
|
.i2c_dev = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \ |
||||||
|
.reg_ipsr = DT_INST_REG_ADDR_BY_IDX(inst, 0), \ |
||||||
|
.reg_sovr = DT_INST_REG_ADDR_BY_IDX(inst, 1), \ |
||||||
|
.reg_gpcr = DT_INST_REG_ADDR_BY_IDX(inst, 2), \ |
||||||
|
.reg_gpisr = DT_INST_REG_ADDR_BY_IDX(inst, 3), \ |
||||||
|
.reg_gpier = DT_INST_REG_ADDR_BY_IDX(inst, 4), \ |
||||||
|
.pin_mask = DT_INST_PROP(inst, pin_mask), \ |
||||||
|
}; \ |
||||||
|
DEVICE_DT_INST_DEFINE(inst, gpio_it8801_init, NULL, &gpio_it8801_data_##inst, \ |
||||||
|
&gpio_it8801_cfg_##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \ |
||||||
|
&gpio_it8801_driver_api); |
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_IT8801_DEVICE_INST) |
@ -0,0 +1,23 @@ |
|||||||
|
# Copyright (c) 2024 ITE Corporation. All Rights Reserved. |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
description: ITE IT8801 I2C-based GPIO device driver |
||||||
|
|
||||||
|
compatible: "ite,it8801-gpio" |
||||||
|
|
||||||
|
include: [base.yaml, gpio-controller.yaml] |
||||||
|
|
||||||
|
properties: |
||||||
|
reg: |
||||||
|
required: true |
||||||
|
|
||||||
|
pin-mask: |
||||||
|
type: int |
||||||
|
required: true |
||||||
|
description: | |
||||||
|
Not every GPIO pins are usable for IT8801. This property |
||||||
|
indicates the usable GPIO pin mask. |
||||||
|
|
||||||
|
gpio-cells: |
||||||
|
- pin |
||||||
|
- flags |
Loading…
Reference in new issue