embox

Форк
0
263 строки · 6.1 Кб
1
/**
2
 * @file gpio.c
3
 * @brief
4
 * @author Alexander Kalmuk
5
 * @date 8.02.2018
6
 */
7

8
#include <util/log.h>
9

10
#include <errno.h>
11
#include <assert.h>
12

13
#include <kernel/lthread/lthread.h>
14
#include <mem/misc/pool.h>
15

16
#include <lib/libds/bit.h>
17
#include <lib/libds/dlist.h>
18
#include <lib/libds/array.h>
19

20
#include <drivers/gpio/gpio.h>
21
#include <drivers/gpio/gpio_driver.h>
22

23
#define GPIO_CHIPS_COUNT  OPTION_GET(NUMBER,gpio_chips_count)
24
#define GPIO_IRQS_COUNT   OPTION_GET(NUMBER,gpio_irqs_count)
25
#define GPIO_HND_PRIORITY OPTION_GET(NUMBER,gpio_hnd_prio)
26

27
ARRAY_SPREAD_DECLARE(struct gpio_chip *, __gpio_chip_registry);
28

29
static struct gpio_chip *gpio_chip_registry[GPIO_CHIPS_COUNT];
30

31
int gpio_register_chip(struct gpio_chip *chip, unsigned char chip_id) {
32
	assert(chip);
33

34
	if (chip_id >= GPIO_CHIPS_COUNT) {
35
		log_error("GPIO chip id=%d exceeds max id=%d\n",
36
			chip_id, GPIO_CHIPS_COUNT);
37
		return -1;
38
	}
39
	if (gpio_chip_registry[chip_id]) {
40
		log_error("GPIO chip id%d is registered already\n", chip_id);
41
		return -1;
42
	}
43
	gpio_chip_registry[chip_id] = chip;
44

45
	return 0;
46
}
47

48
static struct gpio_chip *gpio_get_chip(unsigned char chip_nr) {
49

50
	if ((GPIO_CHIPS_COUNT - 1) < chip_nr) {
51
		return NULL;
52
	}
53
	if (NULL != gpio_chip_registry[chip_nr]) {
54
		return gpio_chip_registry[chip_nr];
55
	}
56

57
	if (ARRAY_SPREAD_SIZE(__gpio_chip_registry) != 0) {
58
		return __gpio_chip_registry[0];
59
	}
60
	return NULL;
61
}
62

63
int gpio_setup_mode(unsigned short port, gpio_mask_t pins, int mode) {
64
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
65

66
	if (!chip) {
67
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
68
		return -EINVAL;
69
	}
70
	port = GPIO_PORT(port);
71
	if (port >= chip->nports) {
72
		log_error("port (%d) is out of range (nports=%d)",
73
			port, chip->nports - 1);
74
		return -EINVAL;
75
	}
76
	assert(chip->setup_mode);
77

78
	return chip->setup_mode(port, pins, mode);
79
}
80

81
void gpio_set(unsigned short port, gpio_mask_t pins, char level) {
82
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
83

84
	if (!chip) {
85
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
86
		return;
87
	}
88
	port = GPIO_PORT(port);
89
	if (port >= chip->nports) {
90
		log_error("port (%d) is out of range (nports=%d)",
91
			port, chip->nports - 1);
92
		return;
93
	}
94
	assert(chip->set);
95
	chip->set(port, pins, level);
96
}
97

98
gpio_mask_t gpio_get(unsigned short port, gpio_mask_t pins) {
99
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
100

101
	if (!chip) {
102
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
103
		return -1;
104
	}
105
	port = GPIO_PORT(port);
106
	if (port >= chip->nports) {
107
		log_error("port (%d) is out of range (nports=%d)",
108
			port, chip->nports - 1);
109
		return -EINVAL;
110
	}
111
	assert(chip->get);
112

113
	return chip->get(port, pins);
114
}
115

116
void gpio_toggle(unsigned short port, gpio_mask_t pins) {
117
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
118
	gpio_mask_t state;
119

120
	if (!chip) {
121
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
122
		return;
123
	}
124
	port = GPIO_PORT(port);
125
	if (port >= chip->nports) {
126
		log_error("port (%d) is out of range (nports=%d)",
127
			port, chip->nports - 1);
128
		return;
129
	}
130
	assert(chip->get && chip->set);
131
	state = chip->get(port, pins);
132
	chip->set(port, state, 0); /* Down all pins that were HIGH */
133
	chip->set(port, ~state & pins, 1); /* Up all pins that were LOW */
134
}
135

136
#if (GPIO_IRQS_COUNT > 0)
137
#define DO_IPL_LOCKED(job)       \
138
	{                            \
139
		ipl_t _ipl = ipl_save(); \
140
		job;                     \
141
		ipl_restore(_ipl);       \
142
	}
143

144
struct gpio_irq_handler {
145
	struct gpio_chip *chip;
146
	unsigned char port;
147
	uint32_t pin;
148
	void *data;
149
	void (*handler)(void *);
150
	struct dlist_head link;
151
};
152

153
static int gpio_lthread_irq_hnd(struct lthread *self);
154
static LTHREAD_DEF(gpio_lthread, gpio_lthread_irq_hnd, GPIO_HND_PRIORITY);
155

156
POOL_DEF(gpio_irq_pool, struct gpio_irq_handler, GPIO_IRQS_COUNT);
157

158
static DLIST_DEFINE(gpio_irq_list);
159
static DLIST_DEFINE(gpio_pending_irq_list);
160

161
int gpio_irq_attach(unsigned short port, uint32_t pin,
162
		void (*pin_handler)(void *), void *data) {
163
	struct gpio_irq_handler *gpio_hnd;
164
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
165

166
	if (!chip) {
167
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
168
		return -EINVAL;
169
	}
170
	port = GPIO_PORT(port);
171
	if (port >= chip->nports) {
172
		log_error("port (%d) is out of range (nports=%d)",
173
			port, chip->nports - 1);
174
		return -EINVAL;
175
	}
176
	gpio_hnd = pool_alloc(&gpio_irq_pool);
177
	if (!gpio_hnd) {
178
		log_error("chip=%d, gpio_hnd=%p",
179
			GPIO_CHIP(port), gpio_hnd);
180
		return -ENOMEM;
181
	}
182

183
	gpio_hnd->chip = chip;
184
	gpio_hnd->port = port;
185
	gpio_hnd->pin  = pin;
186
	gpio_hnd->data = data;
187
	gpio_hnd->handler = pin_handler;
188

189
	DO_IPL_LOCKED(
190
		dlist_add_next(dlist_head_init(&gpio_hnd->link), &gpio_irq_list);
191
	);
192

193
	return 0;
194
}
195

196
int gpio_irq_detach(unsigned short port, uint32_t pin) {
197
	struct gpio_irq_handler *gpio_hnd;
198
	struct gpio_chip *chip = gpio_get_chip(GPIO_CHIP(port));
199

200
	if (!chip) {
201
		log_error("Chip not found, chip=%d", GPIO_CHIP(port));
202
		return -EINVAL;
203
	}
204

205
	port = GPIO_PORT(port);
206

207
	dlist_foreach_entry_safe(gpio_hnd, &gpio_irq_list, link) {
208
		if ((gpio_hnd->chip == chip)
209
				&& (gpio_hnd->port == port)
210
				&& (gpio_hnd->pin  == pin)) {
211
			DO_IPL_LOCKED(dlist_del(&gpio_hnd->link));
212
			pool_free(&gpio_irq_pool, gpio_hnd);
213

214
			return 0;
215
		}
216
	}
217

218
	return -1;
219
}
220

221
static int gpio_lthread_irq_hnd(struct lthread *self) {
222
	struct gpio_irq_handler *gpio_hnd;
223

224
	dlist_foreach_entry_safe(gpio_hnd, &gpio_pending_irq_list, link) {
225
		gpio_hnd->handler(gpio_hnd->data);
226

227
		/* Move handler back to gpio list. */
228
		DO_IPL_LOCKED(
229
			dlist_del(&gpio_hnd->link);
230
			dlist_add_prev(&gpio_hnd->link, &gpio_irq_list);
231
		);
232
	}
233

234
	return 0;
235
}
236

237
void gpio_handle_irq(struct gpio_chip *chip, uint8_t port, gpio_mask_t pins) {
238
	struct gpio_irq_handler *gpio_hnd;
239
	int pin;
240
	int pending = 0;
241

242
	bit_foreach(pin, pins) {
243
		dlist_foreach_entry_safe(gpio_hnd, &gpio_irq_list, link) {
244
			if ((gpio_hnd->chip == chip)
245
					&& (gpio_hnd->port == port)
246
					&& (gpio_hnd->pin  == (1 << pin))) {
247

248
				/* Move handler to pending list to be processed in lthread. */
249
				DO_IPL_LOCKED(
250
					dlist_del(&gpio_hnd->link);
251
					dlist_add_prev(&gpio_hnd->link, &gpio_pending_irq_list);
252
				);
253

254
				pending = 1;
255
			}
256
		}
257
	}
258

259
	if (pending) {
260
		lthread_launch(&gpio_lthread);
261
	}
262
}
263
#endif
264

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.