/
gpio_nct38xx_alert.c
146 lines (121 loc) · 4.89 KB
/
gpio_nct38xx_alert.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
* Copyright (c) 2021 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_nct38xx_gpio_alert
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_nct38xx.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util_macro.h>
#include "gpio_nct38xx.h"
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);
/* Driver config */
struct nct38xx_alert_config {
/* Alert GPIO pin */
const struct gpio_dt_spec irq_gpio;
/* NCT38XX devices which share the same alert pin */
const struct device **nct38xx_dev;
/* Number of NCT38XX devices on the alert pin */
uint32_t nct38xx_num;
};
/* Driver data */
struct nct38xx_alert_data {
/* Alert handler device */
const struct device *alert_dev;
/* Alert pin callback */
struct gpio_callback gpio_cb;
/* Alert worker */
struct k_work alert_worker;
};
static void nct38xx_alert_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
ARG_UNUSED(pins);
struct nct38xx_alert_data *data = CONTAINER_OF(cb, struct nct38xx_alert_data, gpio_cb);
k_work_submit(&data->alert_worker);
}
static void nct38xx_alert_worker(struct k_work *work)
{
struct nct38xx_alert_data *const data =
CONTAINER_OF(work, struct nct38xx_alert_data, alert_worker);
const struct nct38xx_alert_config *const config = data->alert_dev->config;
uint16_t alert, mask;
do {
/* NCT38XX device handler */
for (int i = 0; i < config->nct38xx_num; i++) {
/* Clear alert */
if (nct38xx_reg_burst_read(config->nct38xx_dev[i], NCT38XX_REG_ALERT,
(uint8_t *)&alert, sizeof(alert))) {
LOG_ERR("i2c access failed");
return;
}
if (nct38xx_reg_burst_read(config->nct38xx_dev[i], NCT38XX_REG_ALERT_MASK,
(uint8_t *)&mask, sizeof(mask))) {
LOG_ERR("i2c access failed");
return;
}
alert &= mask;
if (alert) {
if (nct38xx_reg_burst_write(config->nct38xx_dev[i],
NCT38XX_REG_ALERT, (uint8_t *)&alert,
sizeof(alert))) {
LOG_ERR("i2c access failed");
return;
}
}
if (alert & BIT(NCT38XX_REG_ALERT_VENDOR_DEFINDED_ALERT)) {
nct38xx_gpio_alert_handler(config->nct38xx_dev[i]);
}
}
/* While the interrupt signal is still active; we have more work to do. */
} while (gpio_pin_get_dt(&config->irq_gpio));
}
static int nct38xx_alert_init(const struct device *dev)
{
const struct nct38xx_alert_config *const config = dev->config;
struct nct38xx_alert_data *const data = dev->data;
int ret;
/* Check NCT38XX devices are all ready. */
for (int i = 0; i < config->nct38xx_num; i++) {
if (!device_is_ready(config->nct38xx_dev[i])) {
LOG_ERR("%s device not ready", config->nct38xx_dev[i]->name);
return -ENODEV;
}
}
/* Set the alert pin for handling the interrupt */
k_work_init(&data->alert_worker, nct38xx_alert_worker);
if (!device_is_ready(config->irq_gpio.port)) {
LOG_ERR("%s device not ready", config->irq_gpio.port->name);
return -ENODEV;
}
gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT);
gpio_init_callback(&data->gpio_cb, nct38xx_alert_callback, BIT(config->irq_gpio.pin));
ret = gpio_add_callback(config->irq_gpio.port, &data->gpio_cb);
if (ret < 0) {
return ret;
}
gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_EDGE_TO_ACTIVE);
return 0;
}
/* NCT38XX alert driver must be initialized after NCT38XX GPIO driver */
BUILD_ASSERT(CONFIG_GPIO_NCT38XX_ALERT_INIT_PRIORITY > CONFIG_GPIO_NCT38XX_INIT_PRIORITY);
#define NCT38XX_DEV_AND_COMMA(node_id, prop, idx) \
DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),
#define NCT38XX_ALERT_DEVICE_INSTANCE(inst) \
const struct device *nct38xx_dev_##inst[] = { DT_INST_FOREACH_PROP_ELEM( \
inst, nct38xx_dev, NCT38XX_DEV_AND_COMMA) }; \
static const struct nct38xx_alert_config nct38xx_alert_cfg_##inst = { \
.irq_gpio = GPIO_DT_SPEC_INST_GET(inst, irq_gpios), \
.nct38xx_dev = &nct38xx_dev_##inst[0], \
.nct38xx_num = DT_INST_PROP_LEN(inst, nct38xx_dev), \
}; \
static struct nct38xx_alert_data nct38xx_alert_data_##inst = { \
.alert_dev = DEVICE_DT_INST_GET(inst), \
}; \
DEVICE_DT_INST_DEFINE(inst, nct38xx_alert_init, NULL, &nct38xx_alert_data_##inst, \
&nct38xx_alert_cfg_##inst, POST_KERNEL, \
CONFIG_GPIO_NCT38XX_ALERT_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(NCT38XX_ALERT_DEVICE_INSTANCE)