|  | /* | 
|  | * 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) |