1 /*
2 * Copyright (c) 2024 ITE Corporation. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ite_it8801_mfd
8
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/mfd/mfd_ite_it8801.h>
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(mfd_ite_it8801, CONFIG_MFD_LOG_LEVEL);
15
16 struct mfd_it8801_config {
17 const struct i2c_dt_spec i2c_dev;
18 /* Alert GPIO pin */
19 const struct gpio_dt_spec irq_gpios;
20 };
21
22 struct mfd_it8801_data {
23 struct k_work gpio_isr_worker;
24 /* Alert pin callback */
25 struct gpio_callback gpio_cb;
26 sys_slist_t callback_list;
27 };
28
it8801_check_vendor_id(const struct device * dev)29 static int it8801_check_vendor_id(const struct device *dev)
30 {
31 const struct mfd_it8801_config *config = dev->config;
32 int i, ret;
33 uint8_t val;
34
35 /* Verify vendor ID registers(16-bits). */
36 for (i = 0; i < ARRAY_SIZE(it8801_id_verify); i++) {
37 ret = i2c_reg_read_byte_dt(&config->i2c_dev, it8801_id_verify[i].reg, &val);
38
39 if (ret != 0) {
40 LOG_ERR("Failed to read vendoer ID (ret %d)", ret);
41 return ret;
42 }
43
44 if (val != it8801_id_verify[i].chip_id) {
45 LOG_ERR("The IT8801 vendor ID is wrong. Index: %d, Expected ID: 0x%x,"
46 "Read ID: 0x%x",
47 i, it8801_id_verify[i].chip_id, val);
48 return -ENODEV;
49 }
50 }
51
52 return 0;
53 }
54
it8801_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)55 static void it8801_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
56 {
57 ARG_UNUSED(pins);
58 struct mfd_it8801_data *data = CONTAINER_OF(cb, struct mfd_it8801_data, gpio_cb);
59
60 k_work_submit(&data->gpio_isr_worker);
61 }
62
mfd_it8801_register_interrupt_callback(const struct device * mfd,struct it8801_mfd_callback * callback)63 void mfd_it8801_register_interrupt_callback(const struct device *mfd,
64 struct it8801_mfd_callback *callback)
65 {
66 struct mfd_it8801_data *data = mfd->data;
67
68 sys_slist_append(&data->callback_list, &callback->node);
69 }
70
it8801_gpio_alert_worker(struct k_work * work)71 static void it8801_gpio_alert_worker(struct k_work *work)
72 {
73 struct mfd_it8801_data *data = CONTAINER_OF(work, struct mfd_it8801_data, gpio_isr_worker);
74 struct it8801_mfd_callback *cb_entry;
75
76 SYS_SLIST_FOR_EACH_CONTAINER(&data->callback_list, cb_entry, node) {
77 cb_entry->cb(cb_entry->dev);
78 }
79 }
80
mfd_it8801_init(const struct device * dev)81 static int mfd_it8801_init(const struct device *dev)
82 {
83 const struct mfd_it8801_config *config = dev->config;
84 struct mfd_it8801_data *data = dev->data;
85 int ret;
86
87 if (!i2c_is_ready_dt(&config->i2c_dev)) {
88 LOG_ERR("I2C bus %s is not ready", config->i2c_dev.bus->name);
89 return -ENODEV;
90 }
91
92 /* Verify Vendor ID registers. */
93 ret = it8801_check_vendor_id(dev);
94 if (ret) {
95 LOG_ERR("Failed to read IT8801 vendor id %x", ret);
96 return ret;
97 }
98
99 k_work_init(&data->gpio_isr_worker, it8801_gpio_alert_worker);
100
101 sys_slist_init(&data->callback_list);
102
103 /* Alert response enable */
104 ret = i2c_reg_write_byte_dt(&config->i2c_dev, IT8801_REG_SMBCR, IT8801_REG_MASK_ARE);
105 if (ret != 0) {
106 LOG_ERR("Failed to initialization setting (ret %d)", ret);
107 return ret;
108 }
109
110 gpio_pin_configure_dt(&config->irq_gpios, GPIO_INPUT);
111
112 /* Initialize GPIO interrupt callback */
113 gpio_init_callback(&data->gpio_cb, it8801_gpio_callback, BIT(config->irq_gpios.pin));
114
115 ret = gpio_add_callback(config->irq_gpios.port, &data->gpio_cb);
116 if (ret != 0) {
117 LOG_ERR("Failed to add INT callback: %d", ret);
118 return ret;
119 }
120 gpio_pin_interrupt_configure_dt(&config->irq_gpios, GPIO_INT_MODE_EDGE | GPIO_INT_TRIG_LOW);
121
122 return 0;
123 }
124
125 #define MFD_IT8801_DEFINE(inst) \
126 static struct mfd_it8801_data it8801_data_##inst; \
127 static const struct mfd_it8801_config it8801_cfg_##inst = { \
128 .i2c_dev = I2C_DT_SPEC_INST_GET(inst), \
129 .irq_gpios = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, {0}), \
130 }; \
131 DEVICE_DT_INST_DEFINE(inst, mfd_it8801_init, NULL, &it8801_data_##inst, \
132 &it8801_cfg_##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
133
134 DT_INST_FOREACH_STATUS_OKAY(MFD_IT8801_DEFINE)
135