rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
l9779.cpp
Go to the documentation of this file.
1/*
2 * l9779.cpp
3 *
4 * Created on: Jan 10, 2022
5 *
6 * Andrey Gusakov, (c) 2022
7 *
8 * Masks/inputs bits:
9 * 0..3 - IGN1 .. 4 - Ignition pre-drivers
10 * Driven by logical-AND of SPI control bit and dedicated parallel input IGNI1...IGNI4
11 * 4..7 - OUT1 .. 4 - Protected low-side drivers with max current 2.2A
12 * Driven by logical-AND of SPI control bit and dedicated parallel input IN1...IN4
13 * 8 - OUT5 - Protected low-side driver with max current 3A
14 * Driven by logical-AND of SPI control bit and dedicated parallel input IN5
15 * 9..10 - OUT6,7 - Protected low-side drivers with max current 5A (O2 heaters)
16 * Driven by logical-AND of SPI control bit and dedicated parallel input IN6, IN7.
17 * 11 - Unused (no OUT8), IN8-PWM is used for stepper
18 * 12..15 - OUT9..12 - Not exist on L9779WD-SPI, TODO: check L9779WD
19 * 16..17 - OUT13..14 - Protected low side relay drivers with max current 600 mA and Low Battery Volatage function
20 * 18..21 - OUT15..18 - Protected low side relay drivers with max current 600 mA
21 * 22 - Unused (no OUT19)
22 * 23 - OUT20 - Protected low side low current driver with max current 50 mA
23 * 24..27 - OUTA..D - Configurable outputs (OD, PP) with max current 0.6 A (for low and high side FETs)
24 * Can be configured for stepper motor driving.
25 * Stepper is controlled by the logic AND between PWM (IN8) input pin and PWM SPI bit.
26 * 28..31 - OUT25..27 - Unused on L9779WD-SPI, TODO for L9779WD
27 * 32 - MR - Main Relay low side driver with max current 0.6 A, automaticly controlled
28 */
29
30#include "pch.h"
31
32#include "gpio/l9779.h"
33
34#if EFI_PROD_CODE && (BOARD_L9779_COUNT > 0)
35
37#include "hardware.h"
38#include "gpio/gpio_ext.h"
39/*
40 * TODO list:
41 * - just write code
42 */
43/*==========================================================================*/
44/* Driver local definitions. */
45/*==========================================================================*/
46#define DRIVER_NAME "l9779"
47
48#define DIAG_PERIOD_MS (7)
49
56
57/* SPI communication helpers */
58/* Out frame */
59/* D0 - parity */
60/* D8:D1 - DATA OUT or SUBADDRESS if ADD[4:0] = 0x10 (for read) */
61#define MSG_SET_DATA(d) (((d) & 0xff) << 1)
62/* sub-address is 5 bit */
63#define MSG_SET_SUBADDR(s) (((s) & 0x1f) << 1)
64/* D9 - x */
65/* D14:D10 - ADDRESS */
66#define MSG_SET_ADDR(a) (((a) & 0x1f) << 10)
67/* D15 - x */
68
69/* ADD user for read commands */
70#define MSG_READ_ADDR (0x10)
71
72#define MSG_W(a, d) (static_cast<uint16_t>((MSG_SET_ADDR(a) | MSG_SET_DATA(d))))
73#define MSG_R(a) (static_cast<uint16_t>((MSG_SET_ADDR(MSG_READ_ADDR) | MSG_SET_SUBADDR(d))))
74
75/* Both DIN and DO */
76/* D0 - parity */
77#define MSG_GET_PARITY(x) (((x) >> 0) & 0x01)
78/* D14:D10 - Addr of DATA IN or DATA OUT */
79#define MSG_GET_ADDR(x) (((x) >> 10) & 0x1f)
80/* D8:D1 - DATA IN */
81#define MSG_GET_DATA(x) (((x) >> 1) & 0xff)
82
83/* DIN / to chip */
84/* D8:D1 or 5 bits of subaddr in case of read access */
85#define MSG_GET_SUBADDR(tx) (MSG_GET_DATA(tx) & 0x1f)
86
87/* DOUT / from chip */
88/* D 9 - W/R flag, 1 if we read */
89#define MSG_GET_WR(rx) (((rx) >> 9) & 0x01)
90/* D15 - SPI error flag */
91#define MSG_GET_SPIERROR(rx) (((rx) >> 15) & 0x01)
92
93/* register address that never can be replyed */
94#define REG_INVALID 0xff
95
96/* Write only registers */
97#define CMD_CLOCK_UNLOCK_SW_RST(d) MSG_W(0x0c, (d))
98#define CMD_START_REACT(d) MSG_W(0x0d, (d))
99#define CMD_CONTR_REG(n, d) MSG_W(0x08 + (n), (d))
100
101/* Read only registers */
102
103/* IGN1..4 + OUT1..7 */
104#define OUT_DIRECT_DRIVE_MASK 0x7ff
105
106/*==========================================================================*/
107/* Driver exported variables. */
108/*==========================================================================*/
109
110/*==========================================================================*/
111/* Driver local variables and types. */
112/*==========================================================================*/
113
114/* Driver private data */
115struct L9779 : public GpioChip {
116 int init() override;
117 int deinit() override;
118
119 int setPadMode(size_t pin, iomode_t mode) override;
120 int writePad(size_t pin, int value) override;
121 int readPad(size_t pin) override;
122 brain_pin_diag_e getDiag(size_t pin) override;
123
124 bool spi_parity_odd(uint16_t x);
125 int spi_validate(uint16_t rx);
126 int spi_rw(uint16_t tx, uint16_t *rx_ptr);
127 int spi_rw_array(const uint16_t *tx, uint16_t *rx, int n);
128
129 int update_output();
130 int update_direct_output(size_t pin, int value);
131 int wake_driver();
132
133 int chip_reset();
134 int chip_init_data();
135 int chip_init();
136
137 brain_pin_diag_e getOutputDiag(size_t pin);
138 brain_pin_diag_e getInputDiag(size_t pin);
139
140 const l9779_config *cfg;
141
142 /* thread stuff */
143 thread_t *thread;
144 THD_WORKING_AREA(thread_wa, 256);
145 semaphore_t wake;
146
147 /* state to be sent to chip */
148 uint32_t o_state;
149 /* output enabled mask */
150 uint32_t o_oe_mask;
151 /* cached output registers state - value last send to chip */
152 uint32_t o_data_cached;
153
154 l9779_drv_state drv_state;
155
156 /* last accesed register */
157 uint8_t last_addr;
158 /* last requested subaddr in case of read */
159 uint8_t last_subaddr;
160
161
162 /* statistic */
163 //int por_cnt;
164 //int wdr_cnt;
165 //int comfe_cnt;
166 //int init_req_cnt;
167 int spi_cnt;
168 int spi_err_parity; /* parity errors in rx data */
169 int spi_err_frame; /* rx messages with bit 15 set */
170 int spi_err; /* rx messages with incorrect ADDR or WR fields */
171 uint16_t recentTx;
172 uint16_t recentRx;
173};
174
175static L9779 chips[BOARD_L9779_COUNT];
176
177static const char* l9779_pin_names[L9779_SIGNALS] = {
178 "L9779.IGN1", "L9779.IGN2", "L9779.IGN3", "L9779.IGN4",
179 "L9779.OUT1", "L9779.OUT2", "L9779.OUT3", "L9779.OUT4",
180 "L9779.OUT5", "L9779.OUT6", "L9779.OUT7", "L9779.OUT8",
181 "L9779.OUT9", "L9779.OUT10", "L9779.OUT11", "L9779.OUT12",
182 "L9779.OUT13", "L9779.OUT14", "L9779.OUT15", "L9779.OUT16",
183 "L9779.OUT17", "L9779.OUT18", "L9779.OUT19", "L9779.OUT20",
184 "L9779.OUTA", "L9779.OUTB", "L9779.OUTC", "L9779.OUTD",
185 "L9779.OUT25", "L9779.OUT26", "L9779.OUT27", "L9779.OUT28",
186 "L9779.MRD", "L9779.KEY"
187};
188
189/*==========================================================================*/
190/* Driver local functions. */
191/*==========================================================================*/
192
193/* true if parity of input x is odd */
194bool L9779::spi_parity_odd(uint16_t x)
195{
196 x ^= x >> 8;
197 x ^= x >> 4;
198 x ^= x >> 2;
199 x ^= x >> 1;
200
201 return (x & 1);
202}
203
204int L9779::spi_validate(uint16_t rx)
205{
206 if (!spi_parity_odd(rx)) {
207 spi_err_parity++;
208 return -1;
209 }
210
211 if (MSG_GET_SPIERROR(rx)) {
212 /* not clear what does this means */
213 spi_err_frame++;
214 return -1;
215 }
216
217 /* check that correct register is returned */
218 if (last_subaddr != REG_INVALID) {
219 /* MISO DO returns 1 at D9 bit and 5bit sub address in
220 * ADD[4:0] field */
221 if (!MSG_GET_WR(rx)) {
222 return -2;
223 }
224 if (MSG_GET_ADDR(rx) != last_subaddr) {
225 /* unexpected SPI answer */
226 spi_err++;
227
228 /* should ve restart? */
229 //need_init = true;
230
231 return -1;
232 }
233 }
234
235 /* LOCK_UNLOCK_SW_RST */
236 if (last_addr == 0x0c) {
237 /* BIT(0) = LOCK flag */
238 /* START_REACT */
239 } else if (last_addr == 0x0d) {
240 /* BIT(0) = OUT_DIS */
241 }
242
243 return 0;
244}
245
246/**
247 * @returns -1 in case of communication error
248 */
249int L9779::spi_rw(uint16_t tx, uint16_t *rx_ptr)
250{
251 int ret;
252 uint16_t rx;
253 SPIDriver *spi = cfg->spi_bus;
254
255 /* set parity */
256 tx |= !spi_parity_odd(tx);
257
258 /* Acquire ownership of the bus. */
259 spiAcquireBus(spi);
260 /* Setup transfer parameters. */
261 spiStart(spi, &cfg->spi_config);
262 /* Slave Select assertion. */
263 spiSelect(spi);
264 /* Atomic transfer operations. */
265 rx = spiPolledExchange(spi, tx);
266 /* Slave Select de-assertion. */
267 spiUnselect(spi);
268 /* Ownership release. */
269 spiReleaseBus(spi);
270
271 /* statistics and debug */
272 recentTx = tx;
273 recentRx = rx;
274 this->spi_cnt++;
275
276 if (rx_ptr)
277 *rx_ptr = rx;
278
279 /* validate reply */
280 ret = spi_validate(rx);
281 /* save last accessed register */
282 last_addr = MSG_GET_ADDR(recentTx);
283 if (last_addr == MSG_READ_ADDR)
284 last_subaddr = MSG_GET_SUBADDR(recentTx);
285 else
286 last_subaddr = REG_INVALID;
287
288 return ret;
289}
290/**
291 * @return -1 in case of communication error
292 */
293int L9779::spi_rw_array(const uint16_t *tx, uint16_t *rx, int n)
294{
295 int ret = 0;
296 SPIDriver *spi = cfg->spi_bus;
297
298 if (n <= 0) {
299 return -2;
300 }
301
302 /* Acquire ownership of the bus. */
303 spiAcquireBus(spi);
304 /* Setup transfer parameters. */
305 spiStart(spi, &cfg->spi_config);
306
307 for (int i = 0; i < n; i++) {
308 /* Slave Select assertion. */
309 spiSelect(spi);
310 /* data transfer */
311 uint16_t rxdata = spiPolledExchange(spi, tx[i]);
312
313 if (rx)
314 rx[i] = rxdata;
315 /* Slave Select de-assertion. */
316 spiUnselect(spi);
317
318 /* statistic and debug */
319 recentTx = tx[i];
320 recentRx = rxdata;
321 this->spi_cnt++;
322
323 /* validate reply */
324 ret = spi_validate(rxdata);
325 /* save last accessed register */
326 last_addr = MSG_GET_ADDR(recentTx);
327 if (last_addr == MSG_READ_ADDR)
328 last_subaddr = MSG_GET_SUBADDR(recentTx);
329 else
330 last_subaddr = REG_INVALID;
331
332 if (ret < 0)
333 break;
334 }
335 /* Ownership release. */
336 spiReleaseBus(spi);
337
338 /* no errors for now */
339 return ret;
340}
341
342/* use datasheet numbering, starting from 1, skip 4 ignition channels */
343#define OUT_ENABLED(n) (!!(o_state & BIT((n) + L9779_OUTPUTS_IGN - 1)))
344#define SHIFT_N_OUT_TO_M(n, m) (OUT_ENABLED(n) << (m))
345
346/* use datasheet numbering, starting from 1 */
347#define IGN_ENABLED(n) (!!(o_state & BIT((n) - 1)))
348#define SHIFT_N_IGN_TO_M(n, m) (IGN_ENABLED(n) << (m))
349
350int L9779::update_output()
351{
352 int ret;
353 uint8_t regs[4];
354
355 /* set value only for non-direct driven pins */
356 uint32_t o_data = o_state & ~OUT_DIRECT_DRIVE_MASK;
357 /* direct driven outputs are logicaly-AND spi bit and dedicated input
358 * set bits to all enabled direct driven outputs */
359 o_data = o_state | (o_oe_mask & OUT_DIRECT_DRIVE_MASK);
360
361 /* nightmare... briliant mapping */
362 regs[0] =
363 SHIFT_N_OUT_TO_M( 1, 7) | /* bit 7 - OUT1 */
364 SHIFT_N_OUT_TO_M( 2, 6) | /* and so on, refer to datasheet */
365 SHIFT_N_OUT_TO_M( 3, 5) |
366 SHIFT_N_OUT_TO_M( 4, 4) |
367 SHIFT_N_OUT_TO_M( 5, 3) |
368 SHIFT_N_OUT_TO_M(20, 2);
369 regs[1] =
370 SHIFT_N_OUT_TO_M(15, 7) |
371 SHIFT_N_OUT_TO_M(14, 6) |
372 /* reserved + don't care */
373 SHIFT_N_IGN_TO_M( 1, 3) |
374 SHIFT_N_IGN_TO_M( 2, 2) |
375 SHIFT_N_IGN_TO_M( 3, 1) |
376 SHIFT_N_IGN_TO_M( 4, 0);
377 regs[2] =
378 SHIFT_N_OUT_TO_M(22, 7) | /* TODO: stepper DIR */
379 SHIFT_N_OUT_TO_M(21, 6) | /* TODO: stepper enable */
380 SHIFT_N_OUT_TO_M(16, 5) |
381 SHIFT_N_OUT_TO_M(14, 4) |
382 SHIFT_N_OUT_TO_M(17, 3) |
383 SHIFT_N_OUT_TO_M(18, 2) |
384 SHIFT_N_OUT_TO_M( 7, 1) |
385 SHIFT_N_OUT_TO_M( 6, 0);
386 regs[3] =
387 SHIFT_N_OUT_TO_M(28, 5) |
388 SHIFT_N_OUT_TO_M(27, 4) |
389 SHIFT_N_OUT_TO_M(26, 3) |
390 SHIFT_N_OUT_TO_M(25, 2) |
391 SHIFT_N_OUT_TO_M(24, 1) |
392 SHIFT_N_OUT_TO_M(23, 0); /* TODO: stepper PWM */
393 uint16_t tx[] = {
394 /* output enables */
395 CMD_CONTR_REG(0, regs[0]),
396 CMD_CONTR_REG(1, regs[1]),
397 CMD_CONTR_REG(2, regs[2]),
398 CMD_CONTR_REG(3, regs[3])
399 };
400 ret = spi_rw_array(tx, NULL, efi::size(tx));
401
402 if (ret == 0) {
403 /* atomic */
404 o_data_cached = o_data;
405 }
406
407 return ret;
408}
409
410int L9779::update_direct_output(size_t pin, int value)
411{
412 /* no direct-drive gpio is allocated for this output */
413 if (cfg->direct_gpio[pin].port == NULL)
414 return -1;
415
416 if (value)
417 palSetPort(cfg->direct_gpio[pin].port,
418 PAL_PORT_BIT(cfg->direct_gpio[pin].pad));
419 else
420 palClearPort(cfg->direct_gpio[pin].port,
421 PAL_PORT_BIT(cfg->direct_gpio[pin].pad));
422 return 0;
423}
424
425/**
426 * @brief L9779 chip driver wakeup.
427 * @details Wake up driver. Will cause output register update
428 */
429
430int L9779::wake_driver()
431{
432 /* Entering a reentrant critical zone.*/
433 chibios_rt::CriticalSectionLocker csl;
434 chSemSignalI(&wake);
435 if (!port_is_isr_context()) {
436 /**
437 * chSemSignalI above requires rescheduling
438 * interrupt handlers have implicit rescheduling
439 */
440 chSchRescheduleS();
441 }
442
443 return 0;
444}
445
446int L9779::chip_reset() {
447 int ret;
448
449 last_addr = REG_INVALID;
450 last_subaddr = REG_INVALID;
451
452 ret = spi_rw(CMD_CLOCK_UNLOCK_SW_RST(BIT(1)), NULL);
453 /**
454 * ???
455 */
456 chThdSleepMilliseconds(3);
457
458 last_addr = REG_INVALID;
459 last_subaddr = REG_INVALID;
460
461 return ret;
462}
463
464/*==========================================================================*/
465/* Driver thread. */
466/*==========================================================================*/
467
468static THD_FUNCTION(l9779_driver_thread, p) {
469 L9779 *chip = reinterpret_cast<L9779*>(p);
470 sysinterval_t poll_interval = 0;
471
472 chRegSetThreadName(DRIVER_NAME);
473
474 while (1) {
475 int ret;
476 msg_t msg = chSemWaitTimeout(&chip->wake, poll_interval);
477
478 /* should we care about msg == MSG_TIMEOUT? */
479 (void)msg;
480
481 /* default polling interval */
482 poll_interval = TIME_MS2I(DIAG_PERIOD_MS);
483
484 if ((chip->cfg == NULL) ||
485 (chip->drv_state == L9779_DISABLED) ||
486 (chip->drv_state == L9779_FAILED))
487 continue;
488
489#if 0
490 bool wd_happy = chip->wd_happy;
491
492 /* update outputs only if WD is happy */
493 if ((wd_happy) || (1)) {
494 ret = chip->update_output();
495 if (ret) {
496 /* set state to L9779_FAILED? */
497 }
498 }
499
500 ret = chip->wd_feed();
501 if (ret < 0) {
502 /* WD is not happy */
503 continue;
504 }
505 /* happiness state has changed! */
506 if ((chip->wd_happy != wd_happy) && (chip->wd_happy)) {
507 chip->need_init = true;
508 }
509#endif
510
511 if (chip->need_init) {
512 /* clear first, as flag can be raised again during init */
513 chip->need_init = false;
514 /* re-init chip! */
515 chip->chip_init();
516 /* sync pins state */
517 chip->update_output();
518 }
519
520 /* Chip is ready to rock? */
521 if (chip->need_init == false) {
522 /* Just update outputs state */
523 ret = chip->update_output();
524 if (ret) {
525 /* set state to L9779_FAILED? */
526 }
527 }
528#if 0
529 if (chip->diag_ts <= chVTGetSystemTimeX()) {
530 /* this is expensive call, will do a lot of spi transfers... */
531 ret = chip->update_status_and_diag();
532 if (ret) {
533 /* set state to L9779_FAILED or force reinit? */
534 } else {
535 diagResponse.reset();
536 }
537 /* TODO:
538 * Procedure to switch on after failure condition occurred:
539 * - Read out of diagnosis bits
540 * - Second read out to verify that the failure conditions are not
541 * remaining
542 * - Set of the dedicated output enable bit of the affected channel
543 * if the diagnosis bit is not active anymore
544 * - Switch on of the channel */
545
546 chip->diag_ts = chTimeAddX(chVTGetSystemTimeX(), TIME_MS2I(DIAG_PERIOD_MS));
547 }
548 poll_interval = chip->calc_sleep_interval();
549#endif
550 /* default poll_interval */
551 }
552}
553
554/*==========================================================================*/
555/* Driver interrupt handlers. */
556/*==========================================================================*/
557/*==========================================================================*/
558/* Driver exported functions. */
559/*==========================================================================*/
560
561int L9779::setPadMode(unsigned int pin, iomode_t mode) {
562 if (pin >= L9779_SIGNALS)
563 return -1;
564
565 (void)mode;
566
567 return 0;
568}
569
570int L9779::writePad(unsigned int pin, int value) {
571 if (pin >= L9779_OUTPUTS)
572 return -1;
573
574 {
575 chibios_rt::CriticalSectionLocker csl;
576
577 if (value) {
578 o_state |= (1 << pin);
579 } else {
580 o_state &= ~(1 << pin);
581 }
582 }
583
584 /* direct driven? */
585 if (OUT_DIRECT_DRIVE_MASK & BIT(pin)) {
586 return update_direct_output(pin, value);
587 } else {
588 return wake_driver();
589 }
590
591 return 0;
592}
593
594brain_pin_diag_e L9779::getOutputDiag(size_t pin)
595{
596 (void)pin;
597
598 return PIN_OK;
599}
600
601brain_pin_diag_e L9779::getInputDiag(unsigned int pin)
602{
603 (void)pin;
604
605 return PIN_OK;
606}
607
608int L9779::readPad(size_t pin) {
609 if (pin >= L9779_SIGNALS)
610 return -1;
611
612 /* unknown pin */
613 return -1;
614}
615
616brain_pin_diag_e L9779::getDiag(size_t pin)
617{
618 if (pin >= L9779_SIGNALS)
619 return PIN_UNKNOWN;
620
621 if (pin < L9779_OUTPUTS)
622 return getOutputDiag(pin);
623 else
624 return getInputDiag(pin);
625}
626
627
628int L9779::chip_init_data(void)
629{
630 int ret = 0;
631
632 o_oe_mask = 0;
633
634 for (int i = 0; i < L9779_DIRECT_OUTPUTS; i++) {
635 if (cfg->direct_gpio[i].port == NULL)
636 continue;
637
638 /* configure source gpio */
639 ret = gpio_pin_markUsed(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, DRIVER_NAME " DIRECT IO");
640 if (ret) {
641 ret = -1;
642 goto err_gpios;
643 }
644 palSetPadMode(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, PAL_MODE_OUTPUT_PUSHPULL);
645 palClearPort(cfg->direct_gpio[i].port, PAL_PORT_BIT(cfg->direct_gpio[i].pad));
646
647 /* enable output */
648 o_oe_mask |= BIT(i);
649 }
650
651 /* enable all spi-driven ouputs
652 * TODO: add API to enable/disable? */
653 o_oe_mask |= ~OUT_DIRECT_DRIVE_MASK;
654
655 return 0;
656
657err_gpios:
658 /* unmark pins */
659 for (int i = 0; i < L9779_DIRECT_OUTPUTS; i++) {
660 if (cfg->direct_gpio[i].port) {
661 gpio_pin_markUnused(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad);
662 }
663 }
664
665 return ret;
666}
667
668int L9779::chip_init()
669{
670 int ret;
671
672 /* statistic */
673 init_cnt++;
674
675 /* Unlock, while unlocked by default. */
676 ret = spi_rw(CMD_CLOCK_UNLOCK_SW_RST(0), NULL);
677 if (ret)
678 return ret;
679
680 /* Enable power stages */
681 ret = spi_rw(CMD_START_REACT(BIT(1)), NULL);
682 if (ret)
683 return ret;
684
685 /* TODO: add spi communication test: read IDENT_REG */
686
687 return ret;
688}
689
690int L9779::init()
691{
692 int ret;
693
694 /* check for multiple init */
695 if (drv_state != L9779_WAIT_INIT)
696 return -1;
697
698 ret = chip_reset();
699 if (ret)
700 return ret;
701
702 ret = chip_init_data();
703 if (ret)
704 return ret;
705
706 /* force chip init from driver thread */
707 need_init = true;
708
709 /* instance is ready */
710 drv_state = L9779_READY;
711
712 /* init semaphore */
713 chSemObjectInit(&wake, 10);
714
715 /* start thread */
716 thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
717 PRIO_GPIOCHIP, l9779_driver_thread, this);
718
719 return 0;
720}
721
722int L9779::deinit()
723{
724 return 0;
725}
726
727/**
728 * @brief L9779 driver add.
729 * @details Checks for valid config
730 * @return return gpio chip base
731 */
732
733int l9779_add(brain_pin_e base, unsigned int index, const l9779_config *cfg) {
734
735 efiAssert(ObdCode::OBD_PCM_Processor_Fault, cfg != NULL, "L9779CFG", 0)
736
737 /* no config or no such chip */
738 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_L9779_COUNT))
739 return -1;
740
741 L9779* chip = &chips[index];
742
743 /* already initted? */
744 if (chip->cfg)
745 return -1;
746
747 /* config */
748 chip->cfg = cfg;
749 /* reset to defaults */
750 chip->drv_state = L9779_WAIT_INIT;
751
752 /* register */
753 int ret = gpiochip_register(base, DRIVER_NAME, *chip, L9779_SIGNALS);
754 if (ret < 0)
755 return ret;
756
757 /* set default pin names, board init code can rewrite */
759
760 return ret;
761}
762
763#endif /* (BOARD_L9779_COUNT > 0) */
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition core.cpp:186
int gpiochips_setPinNames(brain_pin_e base, const char **names)
Set pins names for registered gpiochip.
Definition core.cpp:266
uint32_t iomode_t
Digital I/O modes.
Definition hal_pal_lld.h:83
static const char * l9779_pin_names[L9779_SIGNALS]
Definition l9779.cpp:177
static THD_FUNCTION(l9779_driver_thread, p)
Definition l9779.cpp:468
l9779_drv_state
Definition l9779.cpp:50
@ L9779_DISABLED
Definition l9779.cpp:51
@ L9779_READY
Definition l9779.cpp:53
@ L9779_FAILED
Definition l9779.cpp:54
@ L9779_WAIT_INIT
Definition l9779.cpp:52
static L9779 chips[BOARD_L9779_COUNT]
Definition l9779.cpp:175
int l9779_add(brain_pin_e base, unsigned int index, const l9779_config *cfg)
L9779 driver add.
Definition l9779.cpp:733
@ OBD_PCM_Processor_Fault
void gpio_pin_markUnused(ioportid_t port, ioportmask_t pin)
bool gpio_pin_markUsed(ioportid_t port, ioportmask_t pin, const char *msg)
brain_pin_diag_e
brain_pin_e pin
Definition stm32_adc.cpp:15
static THD_WORKING_AREA(storageManagerThreadStack, 3 *UTILITY_THREAD_STACK_SIZE)
virtual brain_pin_diag_e getDiag(size_t)
Definition gpio_ext.h:31
virtual int writePad(size_t, int)
Definition gpio_ext.h:28
virtual int readPad(size_t)
Definition gpio_ext.h:29
virtual int deinit()
Definition gpio_ext.h:32
virtual int init()=0
virtual int setPadMode(size_t, iomode_t)
Definition gpio_ext.h:27
SPIDriver * spi_bus
Definition l9779.h:26
static Timer diagResponse
Definition tle8888.cpp:42