drivers: mfd: it8801: Initialize IT8801 multi-function device drivers

The IT8801 is an I/O expander that provides GPIO, PWM, Keyboard
functions via the I2C bus.

Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
This commit is contained in:
Tim Lin 2024-07-18 19:13:40 +08:00 committed by Benjamin Cabé
parent eb985d9133
commit 3de8989852
6 changed files with 362 additions and 0 deletions

View file

@ -16,3 +16,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_MAX31790 mfd_max31790.c)
zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c)
zephyr_library_sources_ifdef(CONFIG_MFD_BD8LB600FS mfd_bd8lb600fs.c)
zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c)
zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801 mfd_ite_it8801.c)

View file

@ -29,5 +29,6 @@ source "drivers/mfd/Kconfig.npm1300"
source "drivers/mfd/Kconfig.npm6001"
source "drivers/mfd/Kconfig.lpflexcomm"
source "drivers/mfd/Kconfig.tle9104"
source "drivers/mfd/Kconfig.it8801"
endif # MFD

View file

@ -0,0 +1,11 @@
# Copyright (c) 2024 ITE Corporation. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
config MFD_ITE_IT8801
bool "ITE IT8801 ioexpander multi-function device driver"
default y
depends on DT_HAS_ITE_IT8801_MFD_ENABLED
select I2C
help
Enable the ITE IT8801 ioexpander multi-function device driver.
This ioexpander provides a GPIO/PWM/Keyboard function via I2C bus.

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2024 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8801_mfd
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/mfd/mfd_ite_it8801.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mfd_ite_it8801, CONFIG_MFD_LOG_LEVEL);
struct mfd_it8801_config {
const struct i2c_dt_spec i2c_dev;
/* Alert GPIO pin */
const struct gpio_dt_spec irq_gpios;
};
struct mfd_it8801_data {
struct k_work gpio_isr_worker;
/* Alert pin callback */
struct gpio_callback gpio_cb;
sys_slist_t callback_list;
};
static int it8801_check_vendor_id(const struct device *dev)
{
const struct mfd_it8801_config *config = dev->config;
int i, ret;
uint8_t val;
/* Verify vendor ID registers(16-bits). */
for (i = 0; i < ARRAY_SIZE(it8801_id_verify); i++) {
ret = i2c_reg_read_byte_dt(&config->i2c_dev, it8801_id_verify[i].reg, &val);
if (ret != 0) {
LOG_ERR("Failed to read vendoer ID (ret %d)", ret);
return ret;
}
if (val != it8801_id_verify[i].chip_id) {
LOG_ERR("The IT8801 vendor ID is wrong. Index: %d, Expected ID: 0x%x,"
"Read ID: 0x%x",
i, it8801_id_verify[i].chip_id, val);
return -ENODEV;
}
}
return 0;
}
static void it8801_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
ARG_UNUSED(pins);
struct mfd_it8801_data *data = CONTAINER_OF(cb, struct mfd_it8801_data, gpio_cb);
k_work_submit(&data->gpio_isr_worker);
}
void mfd_it8801_register_interrupt_callback(const struct device *mfd,
struct it8801_mfd_callback *callback)
{
struct mfd_it8801_data *data = mfd->data;
sys_slist_append(&data->callback_list, &callback->node);
}
static void it8801_gpio_alert_worker(struct k_work *work)
{
struct mfd_it8801_data *data = CONTAINER_OF(work, struct mfd_it8801_data, gpio_isr_worker);
struct it8801_mfd_callback *cb_entry;
SYS_SLIST_FOR_EACH_CONTAINER(&data->callback_list, cb_entry, node) {
cb_entry->cb(cb_entry->dev);
}
}
static int mfd_it8801_init(const struct device *dev)
{
const struct mfd_it8801_config *config = dev->config;
struct mfd_it8801_data *data = dev->data;
int ret;
if (!i2c_is_ready_dt(&config->i2c_dev)) {
LOG_ERR("I2C bus %s is not ready", config->i2c_dev.bus->name);
return -ENODEV;
}
/* Verify Vendor ID registers. */
ret = it8801_check_vendor_id(dev);
if (ret) {
LOG_ERR("Failed to read IT8801 vendor id %x", ret);
return ret;
}
k_work_init(&data->gpio_isr_worker, it8801_gpio_alert_worker);
sys_slist_init(&data->callback_list);
/* Alert response enable */
ret = i2c_reg_write_byte_dt(&config->i2c_dev, IT8801_REG_SMBCR, IT8801_REG_MASK_ARE);
if (ret != 0) {
LOG_ERR("Failed to initialization setting (ret %d)", ret);
return ret;
}
gpio_pin_configure_dt(&config->irq_gpios, GPIO_INPUT);
/* Initialize GPIO interrupt callback */
gpio_init_callback(&data->gpio_cb, it8801_gpio_callback, BIT(config->irq_gpios.pin));
ret = gpio_add_callback(config->irq_gpios.port, &data->gpio_cb);
if (ret != 0) {
LOG_ERR("Failed to add INT callback: %d", ret);
return ret;
}
gpio_pin_interrupt_configure_dt(&config->irq_gpios, GPIO_INT_MODE_EDGE | GPIO_INT_TRIG_LOW);
return 0;
}
#define MFD_IT8801_DEFINE(inst) \
static struct mfd_it8801_data it8801_data_##inst; \
static const struct mfd_it8801_config it8801_cfg_##inst = { \
.i2c_dev = I2C_DT_SPEC_INST_GET(inst), \
.irq_gpios = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, {0}), \
}; \
DEVICE_DT_INST_DEFINE(inst, mfd_it8801_init, NULL, &it8801_data_##inst, \
&it8801_cfg_##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(MFD_IT8801_DEFINE)

View file

@ -0,0 +1,109 @@
# Copyright (c) 2024 ITE Corporation. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
description: |
ITE IT8801 ioexpander multi-function device drivers.
This ioexpander provides a GPIO/PWM/Keyboard function via I2C bus.
An example configuration:
&i2c4 {
status = "okay";
clock-frequency = <I2C_BITRATE_STANDARD>;
pinctrl-0 = <&i2c4_clk_gpe0_default
&i2c4_data_gpe7_default>;
pinctrl-names = "default";
it8801_mfd: it8801@38 {
compatible = "ite,it8801-mfd";
/*
* SMBus address (7-bit without R/W)
* SMB_ADDR pin is 0, SMBus address is 0x38
* SMB_ADDR pin is 1, SMBus address is 0x39
*/
reg = <0x38>;
irq-gpios = <&gpioa 1 0>; /* SMB_INT# */
#address-cells = <1>;
#size-cells = <1>;
ioex_it8801_port0: it8801_port@0 {
compatible = "ite,it8801-gpio";
reg = <0x00 1 /* GPIPSR */
0x05 1 /* GPSOVR */
0x0a 8 /* GPCR */
0x32 1 /* GPISR */
0x37 1>; /* GPIER */
gpio-controller;
#gpio-cells = <2>;
ngpios = <8>;
pin-mask = <0xdb>;
};
ioex_it8801_port1: it8801_port@1 {
compatible = "ite,it8801-gpio";
reg = <0x01 1 /* GPIPSR */
0x06 1 /* GPSOVR */
0x12 8 /* GPCR */
0x33 1 /* GPISR */
0x38 1>; /* GPIER */
gpio-controller;
#gpio-cells = <2>;
ngpios = <8>;
pin-mask = <0x3f>;
};
ioex_it8801_port2: it8801_port@2 {
compatible = "ite,it8801-gpio";
reg = <0x02 1 /* GPIPSR */
0x07 1 /* GPSOVR */
0x1a 8 /* GPCR */
0x34 1 /* GPISR */
0x39 1>; /* GPIER */
gpio-controller;
#gpio-cells = <2>;
ngpios = <8>;
pin-mask = <0x0f>;
};
ioex_it8801_kbd: it8801_kbd@40 {
compatible = "ite,it8801-kbd";
reg = <0x40 1 /* KSOMCR */
0x41 1 /* KSIDR */
0x42 1 /* KSIEER */
0x43 1>; /* KSIIER */
kso-mapping = <0 1 20 3 4 5 6
17 18 16 15 11 12>;
mfdctrl = <&kso18_gp01_default
&kso20_gp23_default>;
row-size = <8>;
col-size = <13>;
kscan_input: kscan-input {
compatible = "zephyr,kscan-input";
};
};
ioex_it8801_pwm: it8801_pwm@90 {
compatible = "ite,it8801-pwm";
mfdctrl = <&pwm7_gp20_default>;
reg = <0x90 1 /* PWMMCR */
0x94 1>; /* PWMDCR */
channel = <7>;
#pwm-cells = <3>;
};
};
};
compatible: "ite,it8801-mfd"
include: i2c-device.yaml
properties:
reg:
required: true
irq-gpios:
type: phandle-array
description: |
An interrupt can be asserted on SMB_INT# pin to notify
the host-side since an effective interrupt occurs.

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2024 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_ITE_IT8801_H_
#define ZEPHYR_INCLUDE_DRIVERS_MFD_ITE_IT8801_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* IC clock and power management controller register fields
*/
/* 0xf9: Gather interrupt status register */
#define IT8801_REG_GISR 0xf9
#define IT8801_REG_MASK_GISR_GKSIIS BIT(6)
/* 0xfb: Gather interrupt enable control register */
#define IT8801_REG_GIECR 0xfb
#define IT8801_REG_MASK_GKSIIE BIT(3)
/*
* General control register fields
*/
#define IT8801_REG_LBVIDR 0xfe
#define IT8801_REG_HBVIDR 0xff
struct it8801_vendor_id_t {
uint8_t chip_id;
uint8_t reg;
};
static const struct it8801_vendor_id_t it8801_id_verify[] = {
{0x12, IT8801_REG_HBVIDR},
{0x83, IT8801_REG_LBVIDR},
};
/*
* SMbus interface register fields
*/
/* 0xfa: SMBus control register */
#define IT8801_REG_SMBCR 0xfa
#define IT8801_REG_MASK_ARE BIT(4)
/*
* GPIO register fields
*/
#define IT8801_GPIOAFS_FUN1 0x0
#define IT8801_GPIOAFS_FUN2 0x01
#define IT8801_GPIOAFS_FUN3 0x02
/* GPIO control register */
/* GPIO direction */
#define IT8801_GPIODIR BIT(5)
/* GPIO input and output type */
#define IT8801_GPIOIOT_OD BIT(4)
#define IT8801_GPIOIOT_INT_FALL BIT(4)
#define IT8801_GPIOIOT_INT_RISE BIT(3)
/* GPIO polarity */
#define IT8801_GPIOPOL BIT(2)
/* GPIO pull-down enable */
#define IT8801_GPIOPDE BIT(1)
/* GPIO pull-up enable */
#define IT8801_GPIOPUE BIT(0)
/*
* Keyboard matrix scan controller register fields
*/
/* 0x40: Keyboard scan out mode control register */
#define IT8801_REG_MASK_KSOSDIC BIT(7)
#define IT8801_REG_MASK_KSE BIT(6)
#define IT8801_REG_MASK_AKSOSC BIT(5)
/*
* PWM register fields
*/
#define PWM_IT8801_FREQ 32895
/* Control push-pull flag */
#define PWM_IT8801_PUSH_PULL BIT(8)
/* 0x5f: PWM output open-drain disable register */
#define IT8801_REG_PWMODDSR 0x5f
/* PWM mode control register */
#define IT8801_PWMMCR_MCR_MASK GENMASK(1, 0)
#define IT8801_PWMMCR_MCR_OFF 0
#define IT8801_PWMMCR_MCR_BLINKING 1
#define IT8801_PWMMCR_MCR_BREATHING 2
#define IT8801_PWMMCR_MCR_ON 3
/* Define the IT8801 MFD interrupt callback function handler */
typedef void (*it8801_callback_handler_t)(const struct device *dev);
struct it8801_mfd_callback {
sys_snode_t node;
it8801_callback_handler_t cb;
const struct device *dev;
};
/* Register the interrupt of IT8801 MFD callback function */
void mfd_it8801_register_interrupt_callback(const struct device *mfd,
struct it8801_mfd_callback *callback);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_ITE_IT8801_H_ */