rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
core.cpp
Go to the documentation of this file.
1/**
2 * @file gpio/core.c
3 * @brief EFI-related GPIO code for external gpio chips
4 *
5 * @date Mar 8, 2019
6 * @author Andrey Gusakov, (c) 2019
7 */
8
9#include "pch.h"
10#include "gpio/gpio_ext.h"
11#include "smart_gpio.h"
12
13#define STRING2(x) #x
14#define STRING(x) STRING2(x)
15#pragma message(STRING(BOARD_EXT_GPIOCHIPS))
16
17#if (BOARD_EXT_GPIOCHIPS > 0)
18
19/*==========================================================================*/
20/* Local definitions. */
21/*==========================================================================*/
22
23/*==========================================================================*/
24/* Exported variables. */
25/*==========================================================================*/
26
27/*==========================================================================*/
28/* Local variables and types. */
29/*==========================================================================*/
30
31/* TODO: change array to list? */
32struct gpiochip {
33 brain_pin_e base;
34 size_t size;
35 GpioChip *chip;
36 const char *name;
37 /* optional names of each gpio */
38 const char **gpio_names;
39};
40
41static gpiochip chips[BOARD_EXT_GPIOCHIPS];
42
43#if EFI_PROD_CODE
44
45/* TODO: move inside gpio chip driver? */
46class external_hardware_pwm : public hardware_pwm {
47public:
48 bool hasInit() const {
49 return m_chip != nullptr;
50 }
51
52 int start(const char* msg, gpiochip* chip, size_t pin, float frequency, float duty) {
53 int ret;
54
55 ret = chip->chip->setPadPWM(pin, frequency, duty);
56 if (ret >= 0) {
57 m_chip = chip;
58 m_pin = pin;
59 m_frequency = frequency;
60 } else {
61 /* This is not an error, will fallback to SW PWM */
62 //firmwareError(ObdCode::CUSTOM_GPIO_CHIP_FAILED_PWM, "Faield to enable PWM mode for chip %s on pin \"%s\"", msg, chip->name, pin);
63 efiPrintf("Faield to enable PWM mode for chip %s on pin %d \"%s\"", chip->name, pin, msg);
64 return -1;
65 }
66 return 0;
67 }
68
69 void setDuty(float duty) override {
70 if (!m_chip) {
71 criticalError("Attempted to set duty on null external PWM device");
72 return;
73 }
74
75 m_chip->chip->setPadPWM(m_pin, m_frequency, duty);
76 }
77
78private:
79 gpiochip* m_chip = nullptr;
80 size_t m_pin = 0;
81 float m_frequency = 0;
82};
83
84/* TODO: is 5 enought? */
85static external_hardware_pwm extPwms[5];
86
87#endif
88
89/*==========================================================================*/
90/* Local functions. */
91/*==========================================================================*/
92
93/**
94 * @return pointer to GPIO device for specified pin
95 */
96static gpiochip *gpiochip_find(brain_pin_e pin)
97{
98 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
99 gpiochip *chip = &chips[i];
100
101 if ((pin >= chip->base) && (pin < (chip->base + chip->size)))
102 return chip;
103 }
104
105 return nullptr;
106}
107
108#if EFI_PROD_CODE
109
110static external_hardware_pwm* gpiochip_getNextPwmDevice() {
111 for (size_t i = 0; i < efi::size(extPwms); i++) {
112 if (!extPwms[i].hasInit()) {
113 return &extPwms[i];
114 }
115 }
116
117 criticalError("Run out of gpiochip PWM devices!");
118 return nullptr;
119}
120
121#endif
122
123/*==========================================================================*/
124/* Exported functions. */
125/*==========================================================================*/
126
127/**
128 * return numeric part of EXTERNAL pin name.
129 * @details
130 */
131
133{
134 gpiochip *chip = gpiochip_find(pin);
135
136 if (chip)
137 return pin - chip->base;
138
139 return EFI_ERROR_CODE;
140}
141
142
143/**
144 * @brief Get external chip name
145 * @details return gpiochip name
146 */
147
149 gpiochip *chip = gpiochip_find(pin);
150
151 if (chip)
152 return chip->name;
153
154 return nullptr;
155}
156
157/**
158 * @brief Get external chip port name
159 * @details return pin name or gpiochip name (if no pins names provided)
160 */
161
163{
164 int offset;
165 gpiochip *chip = gpiochip_find(pin);
166
167 if (chip) {
168 offset = pin - chip->base;
169 if ((chip->gpio_names) && (chip->gpio_names[offset]))
170 return chip->gpio_names[offset];
171 }
172
173 return nullptr;
174}
175
176/**
177 * @brief Register gpiochip
178 * @details should be called from board file. Can be called before os ready.
179 * All chips should be registered before gpiochips_init() called.
180 * returns -101 in case of no free chips left
181 * returns -100 in case of no ops provided, incorrect chip size
182 * returns -102 or -103 in case chip overlaps already registered chip(s)
183 * else returns chip base
184 */
185
186int gpiochip_register(brain_pin_e base, const char *name, GpioChip& gpioChip, size_t size) {
187 /* zero size? */
188 if (!size)
189 return -100;
190
191 /* outside? */
192 if ((base + size - 1 > BRAIN_PIN_LAST) || (base <= BRAIN_PIN_ONCHIP_LAST))
193 return -101;
194
195 /* check for overlap with other chips */
196 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
197 if (chips[i].base != Gpio::Unassigned) {
198 #define in_range(a, b, c) (((a) > (b)) && ((a) < (c)))
199 if (in_range(base, chips[i].base, chips[i].base + chips[i].size))
200 return -102;
201 if (in_range(base + size, chips[i].base, chips[i].base + chips[i].size))
202 return -103;
203 }
204 }
205
206 gpiochip *chip = nullptr;
207
208 /* find free gpiochip struct */
209 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
210 if (chips[i].base == Gpio::Unassigned) {
211 chip = &chips[i];
212 break;
213 }
214 }
215
216 /* no free chips left */
217 if (!chip) {
218 return -104;
219 }
220
221 /* register chip */
222 chip->name = name;
223 chip->chip = &gpioChip;
224 chip->base = base;
225 chip->size = size;
226 chip->gpio_names = nullptr;
227
228 // TODO: this cast seems wrong?
229 return (int)base;
230}
231
232
233/**
234 * @brief Unregister gpiochip
235 * @details removes chip from list
236 * TODO: call deinit?
237 */
238
240{
241 gpiochip *chip = gpiochip_find(base);
242
243 if (!chip)
244 return -105;
245
246 /* gpiochip_find - returns chip if base within its range, but we need it to be base */
247 if (chip->base != base)
248 return -106;
249
250 /* unregister chip */
251 chip->name = nullptr;
252 chip->chip = nullptr;
253 chip->base = Gpio::Unassigned;
254 chip->size = 0;
255 chip->gpio_names = nullptr;
256
257 return 0;
258}
259
260/**
261 * @brief Set pins names for registered gpiochip
262 * @details should be called after chip registration. May be called several times
263 * Names array size should be aqual to chip gpio count
264 */
265
266int gpiochips_setPinNames(brain_pin_e base, const char **names)
267{
268 gpiochip *chip = gpiochip_find(base);
269
270 if (!chip)
271 return -113;
272
273 chip->gpio_names = names;
274
275 return 0;
276}
277
278/**
279 * @brief Init all registered gpiochips
280 * @details will call gpiochip init ops for all registered chips
281 * calles when OS is ready, so gpiochip can start threads, use drivers and so on.
282 */
283
284int gpiochips_init(void) {
285 int pins_added = 0;
286
287 for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
288 gpiochip *chip = &chips[i];
289
290 if (chip->base == Gpio::Unassigned)
291 continue;
292
293 int ret = chip->chip->init();
294 if (ret < 0) {
295 #if EFI_PROD_CODE
296 // todo: adjust unit tests to validate this fatal
297 criticalError("Failed to init chip %d: %d", i, ret);
298 #else
299 chip->base = Gpio::Unassigned;
300 #endif
301 } else {
302 pins_added += chip->size;
303 }
304 }
305
306 return pins_added;
307}
308
309/**
310 * @brief Set pin mode of gpiochip
311 * @details set pad mode for given pin.
312 * return -107 if driver does not implemet setPadMode ops
313 * else return value from gpiochip driver.
314 */
315/* this fuction uses iomode_t that is related to STM32 (or other MCU)
316 * output modes. Use some common enums? */
318{
319 gpiochip *chip = gpiochip_find(pin);
320
321 if (!chip)
322 return -107;
323
324 return chip->chip->setPadMode(pin - chip->base, mode);
325}
326
327/**
328 * @brief Set value to gpio of gpiochip
329 * @details actual output value depent on current gpiochip implementation
330 * for smart switch inactive supposed to be closed switch (no current flows)
331 * returns -1 in case of pin not belong to any gpio chip
332 * returns -1 in case of chip does not support seting output value (input only)
333 * else return value from gpiochip driver;
334 */
335
337#if EFI_PROD_CODE
338extern bool isInHardFaultHandler;
339 // todo: technical debt, how do we turn off smart GPIO?!
341 return -130;
342 }
343#endif // EFI_PROD_CODE
344 gpiochip *chip = gpiochip_find(pin);
345
346 if (!chip) {
347 // todo: make readPad fail in a similar way?
348 criticalError("Failed migration? Time to reset settings? gpiochip not found for pin %d", pin);
349 return -108;
350 }
351
352 return chip->chip->writePad(pin - chip->base, value);
353}
354
355/**
356 * @brief Get value to gpio of gpiochip
357 * @details actual input value depent on current gpiochip implementation
358 * returns -1 in case of pin not belong to any gpio chip
359 * returns -1 in case of chip does not support getting output value (output only)
360 * else return value from gpiochip driver;
361 */
362
364{
365 gpiochip *chip = gpiochip_find(pin);
366
367 if (!chip)
368 return -109;
369
370 return chip->chip->readPad(pin - chip->base);
371}
372
373/**
374 * @brief Get diagnostic for given gpio
375 * @details actual output value depend on gpiochip capabilities
376 * returns PIN_UNKNOWN in case of pin not belong to any gpio chip
377 * returns PIN_OK in case of chip does not support getting diagnostic
378 * else return brain_pin_diag_e from gpiochip driver;
379 */
380
382 gpiochip *chip = gpiochip_find(pin);
383
384 if (!chip)
385 return PIN_UNKNOWN;
386
387 return chip->chip->getDiag(pin - chip->base);
388}
389
390/**
391 * @brief Get total pin count allocated for external gpio chips.
392 * @details Will also include unused pins for chips that was registered
393 * but later fails to init.
394 */
395
397{
398 int i;
399 int cnt = 0;
400
401 for (i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
402 gpiochip *chip = &chips[i];
403
404 if (chip->base == Gpio::Unassigned)
405 continue;
406
407 cnt += chip->size;
408 }
409
410 return cnt;
411}
412
414{
415 int i;
416
417 for (i = 0; i < BOARD_EXT_GPIOCHIPS; i++) {
418 gpiochip *chip = &chips[i];
419
420 if (chip->base == Gpio::Unassigned)
421 continue;
422
423 efiPrintf("%s (base %d, size %d):\n", chip->name, (int)chip->base, chip->size);
424 chip->chip->debug();
425 }
426}
427
428#if EFI_PROD_CODE
429
430/**
431 * @brief Try to init PWM on given pin
432 * @details success of call depends on chip capabilities
433 * returns nullptr in case there is no chip for given pin
434 * returns nullptr in case of pin is not PWM capable
435 * returns nullptr in case all extPwms are already used
436 * returns hardware_pwm if succes, later user can call ->setDuty to change duty
437 */
438
440{
441 gpiochip *chip = gpiochip_find(pin);
442
443 if (!chip) {
444 return nullptr;
445 }
446
447 /* TODO: implement reintialization of same pin with different settings reusing same external_hardware_pwm */
448 if (external_hardware_pwm *device = gpiochip_getNextPwmDevice()) {
449 if (device->start(msg, chip, pin - chip->base, frequency, duty) >= 0) {
450 return device;
451 }
452 }
453
454 return nullptr;
455}
456
457#endif
458
459#else /* BOARD_EXT_GPIOCHIPS > 0 */
460
462 (void)pin;
463
464 return -111;
465}
466
468 (void)pin;
469
470 return nullptr;
471}
472
474 (void)pin;
475
476 return nullptr;
477}
478
479int gpiochip_register(brain_pin_e base, const char *name, GpioChip&, size_t size) {
480 (void)base; (void)name; (void)size;
481
482 return 0;
483}
484
485int gpiochips_setPinNames(brain_pin_e pin, const char **names)
486{
487 (void)pin; (void)names;
488
489 return 0;
490}
491
492int gpiochips_init(void) {
493 return 0;
494}
495
497{
498 return 0;
499}
500
501void gpiochips_debug(void)
502{
503}
504
505#endif /* BOARD_EXT_GPIOCHIPS > 0 */
@ Unassigned
int gpiochips_readPad(brain_pin_e pin)
Get value to gpio of gpiochip.
Definition core.cpp:363
int gpiochip_unregister(brain_pin_e base)
Unregister gpiochip.
Definition core.cpp:239
int gpiochips_setPadMode(brain_pin_e pin, iomode_t mode)
Set pin mode of gpiochip.
Definition core.cpp:317
const char * gpiochips_getChipName(brain_pin_e pin)
Get external chip name.
Definition core.cpp:148
static gpiochip * gpiochip_find(brain_pin_e pin)
Definition core.cpp:96
static external_hardware_pwm extPwms[5]
Definition core.cpp:85
int gpiochips_init(void)
Init all registered gpiochips.
Definition core.cpp:284
int gpiochips_writePad(brain_pin_e pin, int value)
Set value to gpio of gpiochip.
Definition core.cpp:336
static external_hardware_pwm * gpiochip_getNextPwmDevice()
Definition core.cpp:110
int gpiochips_get_total_pins(void)
Get total pin count allocated for external gpio chips.
Definition core.cpp:396
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition core.cpp:186
void gpiochips_debug(void)
Definition core.cpp:413
hardware_pwm * gpiochip_tryInitPwm(const char *msg, brain_pin_e pin, float frequency, float duty)
Try to init PWM on given pin.
Definition core.cpp:439
const char * gpiochips_getPinName(brain_pin_e pin)
Get external chip port name.
Definition core.cpp:162
static gpiochip chips[BOARD_EXT_GPIOCHIPS]
Definition core.cpp:41
int gpiochips_setPinNames(brain_pin_e base, const char **names)
Set pins names for registered gpiochip.
Definition core.cpp:266
brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin)
Get diagnostic for given gpio.
Definition core.cpp:381
int gpiochips_getPinOffset(brain_pin_e pin)
Definition core.cpp:132
bool isInHardFaultHandler
uint32_t iomode_t
Digital I/O modes.
Definition hal_pal_lld.h:83
static Lps25 device
Definition init_baro.cpp:4
static float frequency
Definition init_flex.cpp:21
brain_pin_diag_e
brain_pin_e pin
Definition stm32_adc.cpp:15
virtual void setDuty(float duty)=0
static float duty
composite packet size
uint16_t offset
Definition tunerstudio.h:0