1 /*
2 * Copyright 2024 NXP
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT adi_adp5585
7
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/gpio/gpio_utils.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/byteorder.h>
14
15 #include <zephyr/drivers/mfd/adp5585.h>
16
17 LOG_MODULE_REGISTER(adp5585, CONFIG_GPIO_LOG_LEVEL);
18
mfd_adp5585_software_reset(const struct device * dev)19 static int mfd_adp5585_software_reset(const struct device *dev)
20 {
21 const struct mfd_adp5585_config *config = dev->config;
22 int ret = 0;
23
24 /** Set CONFIG to gpio by default */
25 uint8_t pin_config_buf[] = { ADP5585_PIN_CONFIG_A, 0x00U, 0x00U };
26
27 ret = i2c_write_dt(&config->i2c_bus, pin_config_buf, sizeof(pin_config_buf));
28 if (ret) {
29 goto out;
30 }
31
32 out:
33 if (ret) {
34 LOG_ERR("%s: software reset failed: %d", dev->name, ret);
35 }
36 return ret;
37 }
38
mfd_adp5585_int_gpio_handler(const struct device * dev,struct gpio_callback * gpio_cb,uint32_t pins)39 static void mfd_adp5585_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb,
40 uint32_t pins)
41 {
42 ARG_UNUSED(dev);
43 ARG_UNUSED(pins);
44
45 struct mfd_adp5585_data *data = CONTAINER_OF(gpio_cb, struct mfd_adp5585_data, int_gpio_cb);
46
47 k_work_submit(&data->work);
48 }
49
mfd_adp5585_work_handler(struct k_work * work)50 static void mfd_adp5585_work_handler(struct k_work *work)
51 {
52 struct mfd_adp5585_data *data = CONTAINER_OF(work, struct mfd_adp5585_data, work);
53 const struct mfd_adp5585_config *config = data->dev->config;
54 uint8_t reg_int_status;
55 int ret = 0;
56
57 k_sem_take(&data->lock, K_FOREVER);
58 /* Read Interrput Flag */
59 if (ret == 0) {
60 ret = i2c_reg_read_byte_dt(&config->i2c_bus, ADP5585_INT_STATUS, ®_int_status);
61 }
62 /* Clear Interrput Flag */
63 if (ret == 0) {
64 ret = i2c_reg_write_byte_dt(&config->i2c_bus, ADP5585_INT_STATUS, reg_int_status);
65 }
66
67 k_sem_give(&data->lock);
68
69 #ifdef CONFIG_GPIO_ADP5585
70 if ((reg_int_status & ADP5585_INT_GPI) && device_is_ready(data->child.gpio_dev)) {
71 (void)gpio_adp5585_irq_handler(data->child.gpio_dev);
72 }
73 #endif /* CONFIG_GPIO_ADP5585 */
74 }
75
mfd_adp5585_init(const struct device * dev)76 static int mfd_adp5585_init(const struct device *dev)
77 {
78 const struct mfd_adp5585_config *config = dev->config;
79 struct mfd_adp5585_data *data = dev->data;
80 int ret;
81
82 if (!i2c_is_ready_dt(&config->i2c_bus)) {
83 return -ENODEV;
84 }
85
86 /* reset gpio can be left float */
87 if (gpio_is_ready_dt(&config->reset_gpio)) {
88 ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
89 if (ret) {
90 LOG_ERR("%s: configure reset pin failed: %d", dev->name, ret);
91 return ret;
92 }
93 } else {
94 LOG_WRN("%s: reset pin not configured", dev->name);
95 }
96
97 ret = mfd_adp5585_software_reset(dev);
98 if (ret) {
99 return ret;
100 }
101
102 if (gpio_is_ready_dt(&config->nint_gpio)) {
103 ret = gpio_pin_configure_dt(&config->nint_gpio, GPIO_INPUT);
104 if (ret < 0) {
105 return ret;
106 }
107 ret = gpio_pin_interrupt_configure_dt(&config->nint_gpio, GPIO_INT_EDGE_TO_ACTIVE);
108 if (ret != 0) {
109 LOG_ERR("%s: failed to configure INT interrupt: %d", dev->name, ret);
110 return ret;
111 }
112
113 gpio_init_callback(&data->int_gpio_cb, mfd_adp5585_int_gpio_handler,
114 BIT(config->nint_gpio.pin));
115 ret = gpio_add_callback_dt(&config->nint_gpio, &data->int_gpio_cb);
116 if (ret != 0) {
117 LOG_ERR("%s: failed to add INT callback: %d", dev->name, ret);
118 return ret;
119 }
120 } else {
121 LOG_WRN("%s: nint pin not configured", dev->name);
122 }
123
124 LOG_DBG("%s: init ok\r\n", dev->name);
125
126 return 0;
127 }
128
129 #define MFD_ADP5585_DEFINE(inst) \
130 static const struct mfd_adp5585_config mfd_adp5585_config_##inst = { \
131 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
132 .nint_gpio = GPIO_DT_SPEC_INST_GET_OR(n, nint_gpios, {0}), \
133 .i2c_bus = I2C_DT_SPEC_INST_GET(inst), \
134 }; \
135 static struct mfd_adp5585_data mfd_adp5585_data_##inst = { \
136 .work = Z_WORK_INITIALIZER(mfd_adp5585_work_handler), \
137 .lock = Z_SEM_INITIALIZER(mfd_adp5585_data_##inst.lock, 1, 1), \
138 .dev = DEVICE_DT_INST_GET(inst), \
139 }; \
140 \
141 DEVICE_DT_INST_DEFINE(inst, mfd_adp5585_init, NULL, &mfd_adp5585_data_##inst, \
142 &mfd_adp5585_config_##inst, POST_KERNEL, \
143 CONFIG_MFD_ADP5585_INIT_PRIORITY, NULL);
144
145 DT_INST_FOREACH_STATUS_OKAY(MFD_ADP5585_DEFINE);
146