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:
parent
eb985d9133
commit
3de8989852
6 changed files with 362 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
drivers/mfd/Kconfig.it8801
Normal file
11
drivers/mfd/Kconfig.it8801
Normal 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.
|
||||
134
drivers/mfd/mfd_ite_it8801.c
Normal file
134
drivers/mfd/mfd_ite_it8801.c
Normal 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)
|
||||
109
dts/bindings/mfd/ite,it8801-mfd.yaml
Normal file
109
dts/bindings/mfd/ite,it8801-mfd.yaml
Normal 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.
|
||||
106
include/zephyr/drivers/mfd/mfd_ite_it8801.h
Normal file
106
include/zephyr/drivers/mfd/mfd_ite_it8801.h
Normal 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_ */
|
||||
Loading…
Reference in a new issue