Skip to content

Commit 97478d5

Browse files
valeriosettifabiobaltieri
authored andcommitted
drivers: mfd: axp2101: add irq and power button support
Add initial support for interrupt generated from the axp2101. For the time being only support for power button is added but others can be added in the future if necessary. Signed-off-by: Valerio Setti <[email protected]>
1 parent 71e28e2 commit 97478d5

File tree

3 files changed

+210
-2
lines changed

3 files changed

+210
-2
lines changed

drivers/mfd/Kconfig.axp2101

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,13 @@ config MFD_AXP2101
88
select I2C
99
help
1010
Enable the X-Powers AXP2101 PMIC multi-function device driver
11+
12+
config MFD_AXP2101_POWER_BUTTON
13+
bool "AXP2101 power button"
14+
depends on INPUT
15+
depends on MFD_AXP2101
16+
default y if $(dt_compat_any_has_prop,$(DT_COMPAT_X_POWERS_AXP2101),int-gpios)
17+
help
18+
Enable support for the Power Button available on the AXP2101. This
19+
feature is based on IRQ support from AXP2101 which requires "int-gpios"
20+
device-tree property to be defined in AXP2101 device node.

drivers/mfd/mfd_axp2101.c

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,200 @@
77
#include <stdbool.h>
88

99
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/drivers/gpio.h>
1011
#include <zephyr/sys/util.h>
1112
#include <zephyr/logging/log.h>
13+
#include <zephyr/input/input.h>
1214

1315
LOG_MODULE_REGISTER(mfd_axp2101, CONFIG_MFD_LOG_LEVEL);
1416

17+
/* Helper macro to enable IRQ management from the device-tree. */
18+
#if DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(x_powers_axp2101, int_gpios)
19+
#define MFD_AXP2101_INTERRUPT 1
20+
#endif
21+
1522
struct mfd_axp2101_config {
1623
struct i2c_dt_spec i2c;
24+
struct gpio_dt_spec int_gpio;
25+
};
26+
27+
#if MFD_AXP2101_INTERRUPT
28+
struct mfd_axp2101_data {
29+
struct gpio_callback gpio_cb;
30+
const struct device *dev;
31+
struct k_work work;
1732
};
33+
#endif /* MFD_AXP2101_INTERRUPT */
1834

19-
/* Registers */
35+
/* Registers and (some) corresponding values */
2036
#define AXP2101_REG_CHIP_ID 0x03U
2137
#define AXP2101_CHIP_ID 0x4AU
38+
#define AXP2101_REG_IRQ_ENABLE_0 0x40U
39+
#define AXP2101_REG_IRQ_ENABLE_1 0x41U
40+
#define AXP2101_IRQ_ENABLE_1_PWR_ON_NEG_EDGE_IRQ BIT(1)
41+
#define AXP2101_IRQ_ENABLE_1_PWR_ON_POS_EDGE_IRQ BIT(0)
42+
#define AXP2101_REG_IRQ_ENABLE_2 0x42U
43+
#define AXP2101_REG_IRQ_STATUS_0 0x48U
44+
#define AXP2101_REG_IRQ_STATUS_1 0x49U
45+
#define AXP2101_IRQ_STATUS_1_PWR_ON_NEG_EDGE_IRQ BIT(1)
46+
#define AXP2101_IRQ_STATUS_1_PWR_ON_POS_EDGE_IRQ BIT(0)
47+
#define AXP2101_REG_IRQ_STATUS_2 0x4AU
48+
49+
#if MFD_AXP2101_INTERRUPT
50+
51+
enum irq_status_reg_idx {
52+
AXP2101_IRQ_STATUS_0_IDX = 0,
53+
AXP2101_IRQ_STATUS_1_IDX,
54+
AXP2101_IRQ_STATUS_2_IDX,
55+
AXP2101_IRQ_STATUS_REG_COUNT
56+
};
57+
58+
static const uint8_t axp2101_dflt_irq_enable[] = {
59+
/* AXP2101_REG_IRQ_ENABLE_0 */
60+
0x00,
61+
62+
/* AXP2101_REG_IRQ_ENABLE_1 */
63+
#if CONFIG_MFD_AXP2101_POWER_BUTTON
64+
AXP2101_IRQ_ENABLE_1_PWR_ON_NEG_EDGE_IRQ |
65+
AXP2101_IRQ_ENABLE_1_PWR_ON_POS_EDGE_IRQ,
66+
#else
67+
0x00,
68+
#endif
69+
70+
/* AXP2101_REG_IRQ_ENABLE_2 */
71+
0x00,
72+
};
73+
74+
static inline int axp2101_irq_read_and_clear(const struct i2c_dt_spec *i2c,
75+
uint8_t irq_status[AXP2101_IRQ_STATUS_REG_COUNT])
76+
{
77+
int ret;
78+
79+
ret = i2c_burst_read_dt(i2c, AXP2101_REG_IRQ_STATUS_0, irq_status,
80+
AXP2101_IRQ_STATUS_REG_COUNT);
81+
if (ret < 0) {
82+
LOG_ERR("Failed to read IRQ status registers");
83+
return ret;
84+
}
85+
86+
/* Writing a '1' on a status bit which is already set clears it. Therefore
87+
* to clear all (and only) the bits that have just been dumped we write
88+
* the read values back.
89+
*/
90+
ret = i2c_burst_write_dt(i2c, AXP2101_REG_IRQ_STATUS_0, irq_status,
91+
AXP2101_IRQ_STATUS_REG_COUNT);
92+
if (ret < 0) {
93+
LOG_ERR("Failed to clear IRQ status registers");
94+
return ret;
95+
}
96+
97+
return 0;
98+
}
99+
100+
static void axp2101_k_work_handler(struct k_work *work)
101+
{
102+
struct mfd_axp2101_data *data = CONTAINER_OF(work, struct mfd_axp2101_data, work);
103+
const struct device *dev = data->dev;
104+
const struct mfd_axp2101_config *config = dev->config;
105+
uint8_t irq_status_regs[AXP2101_IRQ_STATUS_REG_COUNT];
106+
int ret;
107+
108+
ret = axp2101_irq_read_and_clear(&config->i2c, irq_status_regs);
109+
if (ret < 0) {
110+
goto exit;
111+
}
112+
113+
#if CONFIG_MFD_AXP2101_POWER_BUTTON
114+
if (irq_status_regs[AXP2101_IRQ_STATUS_1_IDX] & AXP2101_IRQ_STATUS_1_PWR_ON_NEG_EDGE_IRQ) {
115+
ret = input_report_key(dev, INPUT_KEY_POWER, true, true, K_FOREVER);
116+
}
117+
if (irq_status_regs[AXP2101_IRQ_STATUS_1_IDX] & AXP2101_IRQ_STATUS_1_PWR_ON_POS_EDGE_IRQ) {
118+
ret = input_report_key(dev, INPUT_KEY_POWER, false, true, K_FOREVER);
119+
}
120+
if (ret < 0) {
121+
LOG_ERR("Failed to send input event");
122+
}
123+
#endif /* CONFIG_MFD_AXP2101_POWER_BUTTON */
124+
125+
exit:
126+
/* Resubmit work if interrupt is still active */
127+
if (gpio_pin_get_dt(&config->int_gpio) != 0) {
128+
k_work_submit(&data->work);
129+
}
130+
}
131+
132+
static void axp2101_interrupt_callback(const struct device *gpio_dev, struct gpio_callback *cb,
133+
uint32_t pins)
134+
{
135+
struct mfd_axp2101_data *data = CONTAINER_OF(cb, struct mfd_axp2101_data, gpio_cb);
136+
137+
ARG_UNUSED(gpio_dev);
138+
ARG_UNUSED(pins);
139+
140+
k_work_submit(&data->work);
141+
}
142+
143+
static int mfd_axp2101_configure_irq(const struct device *dev)
144+
{
145+
const struct mfd_axp2101_config *config = dev->config;
146+
struct mfd_axp2101_data *data = dev->data;
147+
uint8_t dummy_buffer[] = {0xFF, 0xFF, 0xFF};
148+
int ret;
149+
150+
if (!gpio_is_ready_dt(&config->int_gpio)) {
151+
LOG_WRN("Interrupt GPIO not ready");
152+
return -EIO;
153+
};
154+
155+
k_work_init(&data->work, axp2101_k_work_handler);
156+
data->dev = dev;
157+
158+
/* Enable only selected interrupts (most are enabled by default) */
159+
ret = i2c_burst_write_dt(&config->i2c, AXP2101_REG_IRQ_ENABLE_0,
160+
axp2101_dflt_irq_enable,
161+
ARRAY_SIZE(axp2101_dflt_irq_enable));
162+
if (ret < 0) {
163+
LOG_ERR("Failed to configure enabled IRQs");
164+
return ret;
165+
}
166+
167+
/* Clear any pending interrupt (if any) */
168+
ret = i2c_burst_write_dt(&config->i2c, AXP2101_REG_IRQ_STATUS_0,
169+
dummy_buffer, ARRAY_SIZE(dummy_buffer));
170+
if (ret < 0) {
171+
LOG_ERR("Failed to clear IRQ status registers");
172+
return ret;
173+
}
174+
175+
ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
176+
if (ret < 0) {
177+
LOG_ERR("Failed to configure interrupt GPIO: %d", ret);
178+
return ret;
179+
}
180+
181+
gpio_init_callback(&data->gpio_cb, axp2101_interrupt_callback, BIT(config->int_gpio.pin));
182+
183+
ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb);
184+
if (ret < 0) {
185+
LOG_ERR("Failed to add GPIO callback: %d", ret);
186+
return ret;
187+
}
188+
189+
ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
190+
if (ret < 0) {
191+
LOG_ERR("Failed to configure GPIO interrupt: %d", ret);
192+
return ret;
193+
}
194+
195+
/* Manually kick the work the 1st time if IRQ line is already asserted. */
196+
if (gpio_pin_get_dt(&config->int_gpio) != 0) {
197+
k_work_submit(&data->work);
198+
}
199+
200+
return 0;
201+
}
202+
203+
#endif /* MFD_AXP2101_INTERRUPT */
22204

23205
static int mfd_axp2101_init(const struct device *dev)
24206
{
@@ -41,15 +223,26 @@ static int mfd_axp2101_init(const struct device *dev)
41223
return -EINVAL;
42224
}
43225

226+
#if MFD_AXP2101_INTERRUPT
227+
ret = mfd_axp2101_configure_irq(dev);
228+
if (ret != 0) {
229+
return ret;
230+
}
231+
#endif /* MFD_AXP2101_INTERRUPT */
232+
44233
return 0;
45234
}
46235

47236
#define MFD_AXP2101_DEFINE(node) \
48237
static const struct mfd_axp2101_config config##node = { \
49238
.i2c = I2C_DT_SPEC_GET(node), \
239+
.int_gpio = GPIO_DT_SPEC_GET_OR(node, int_gpios, {0}) \
50240
}; \
51241
\
52-
DEVICE_DT_DEFINE(node, mfd_axp2101_init, NULL, NULL, \
242+
IF_ENABLED(MFD_AXP2101_INTERRUPT, (static struct mfd_axp2101_data data##node;)) \
243+
\
244+
DEVICE_DT_DEFINE(node, mfd_axp2101_init, NULL, \
245+
COND_CODE_1(MFD_AXP2101_INTERRUPT, (&data##node), (NULL)), \
53246
&config##node, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
54247

55248
DT_FOREACH_STATUS_OKAY(x_powers_axp2101, MFD_AXP2101_DEFINE);

dts/bindings/mfd/x-powers,axp2101.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ bus: axp2101
1212
properties:
1313
reg:
1414
required: true
15+
16+
int-gpios:
17+
type: phandle-array
18+
description: |
19+
Interrupt GPIO from the AXP2101 device

0 commit comments

Comments
 (0)