rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
mc33810.cpp
Go to the documentation of this file.
1/*
2 * mc33810.c
3 *
4 * Automotive Engine Control IC
5 * MCZ33810
6 * eight channel output driver: four integrated low-side drivers and four low-side gate pre-drivers.
7 *
8 *
9 * @date Dec 29, 2018
10 * @author Andrey Belomutskiy, (c) 2012-2020
11 *
12 * @date Jan 03, 2020
13 * @author Andrey Gusakov <dron0gus@gmail.com>, (c) 2020
14 *
15 * Masks bits/inputs numbers:
16 * 0..3 - OUT1 .. 3 - Low-Side Injector drivers, 4.5A max
17 * driven through DIN0 .. 3 or SPI (not supported)
18 * 4..7 - GD0 .. 3 - Gate Driver outputs - IGBT or MOSFET pre-drivers,
19 * driven throug GIN0 .. 3 or SPI in GPGD mode (not supported)
20 */
21
22#include "pch.h"
23#include "gpio/gpio_ext.h"
24#include "gpio/mc33810.h"
25
27
28#if EFI_PROD_CODE && (BOARD_MC33810_COUNT > 0)
29
30// For exti irq
31#include "digital_input_exti.h"
32
33/*
34 * TODO list:
35 *
36 */
37
38/*==========================================================================*/
39/* Driver local definitions. */
40/*==========================================================================*/
41
42#define DRIVER_NAME "mc33810"
43
50
56
57#define MC_CMD_READ_REG(reg) (0x0a00 | (((reg) & 0x0f) << 4))
58#define MC_CMD_SPI_CHECK (0x0f00)
59#define MC_CMD_MODE_SELECT(mode) (0x1000 | ((mode) & 0x0fff))
60/* unused
61#define MC_CMD_LSD_FAULT(en) (0x2000 | ((en) & 0x0fff))
62*/
63#define MC_CMD_DRIVER_EN(en) (0x3000 | ((en) & 0x00ff))
64#define MC_CMD_SPARK(spark) (0x4000 | ((spark) & 0x0fff))
65/* unused
66#define MC_CMD_END_SPARK_FILTER(filt) (0x5000 | ((filt) & 0x0003))
67*/
68#define MC_CMD_DAC(dac) (0x6000 | ((dac) & 0x0fff))
69/* unused
70#define MC_CMD_GPGD_SHORT_THRES(sh) (0x7000 | ((sh) & 0x0fff))
71#define MC_CMD_GPGD_SHORT_DUR(dur) (0x8000 | ((dur) & 0x0fff))
72#define MC_CMD_GPGD_FAULT_OP(op) (0x9000 | ((op) & 0x0f0f))
73*/
74#define MC_CMD_PWM(pwm) (0xa000 | ((pwm) & 0x0fff))
75/* unused
76#define MC_CMD_CLK_CALIB (0xe000)
77*/
78
79#define MC_CMD_INVALID (0xf000)
80
81/* get command code from TX value */
82#define TX_GET_CMD(v) (((v) >> 12) & 0x000f)
83
84/* enum? */
85#define REG_ALL_STAT (0x0)
86#define REG_OUT10_FAULT (0x1)
87#define REG_OUT32_FAULT (0x2)
88#define REG_GPGD_FAULT (0x3)
89#define REG_IGN_FAULT (0x4)
90#define REG_MODE_CMD (0x5)
91#define REG_LSD_FAULT_CMD (0x6)
92#define REG_DRIVER_EN (0x7)
93#define REG_SPARK_CMD (0x8)
94#define REG_END_SPARK_FILTER (0x9)
95#define REG_DAC_CMD (0xa)
96#define REG_GPGD_SHORT_THRES (0xb)
97#define REG_GPGD_SHORT_TIMER (0xc)
98#define REG_GPGD_FAULT_OP (0xd)
99#define REG_PWM (0xe)
100#define REG_REV (0xf)
101
102/* 0000.1101.0000.1010b */
103#define SPI_CHECK_ACK (0x0d0a)
104
105#define REP_FLAG_RESET BIT(15)
106/* Command Out of Range */
107#define REP_FLAG_COR BIT(14)
108/* Supply Out of Range */
109#define REP_FLAG_SOR BIT(13)
110/* Over Voltage (Vpwr > 39V) */
111#define REP_FLAG_OV BIT(13)
112/* Low Voltage (Vpwr < 5.3 .. 8.99V) */
113#define REP_FLAG_LV BIT(12)
114/* Some V10-specific Fault */
115#define REP_FLAG_NMF BIT(12)
116
117/*==========================================================================*/
118/* Driver exported variables. */
119/*==========================================================================*/
120
121/*==========================================================================*/
122/* Driver local variables and types. */
123/*==========================================================================*/
124
125/* OS */
126static thread_t *mc33810_thread = NULL;
127SEMAPHORE_DECL(mc33810_wake, 10 /* or BOARD_MC33810_COUNT ? */);
128static THD_WORKING_AREA(mc33810_thread_wa, 256);
129
130#define INJ_MASK 0x0f
131#define IGN_MASK 0xf0
132
133/* Driver */
134struct Mc33810 : public GpioChip, public mc33810_state_s {
135 int init() override;
136
137 int writePad(size_t pin, int value) override;
138 brain_pin_diag_e getDiag(size_t pin) override;
139 void debug() override;
140
141 // internal functions
142 int spi_unselect();
143 int spi_rw(uint16_t tx, uint16_t* rx);
144 int spi_rw_array(const uint16_t *tx, uint16_t *rx, int n);
145 int update_output_and_diag();
146
147 int chip_init();
148 void wake_driver();
149
150 void ign_event(size_t pin, int value);
151 void on_spkdur(efitick_t now);
152
153 int chip_init_data();
154
155 const mc33810_config *cfg;
156
157 /* cached output state - state last send to chip */
158 uint8_t o_state_cached;
159 /* state to be sent to chip */
160 uint8_t o_state;
161 /* direct driven output mask */
162 uint8_t o_direct_mask;
163 /* IGN/GPGD mode bits: [7:4] - GP3..GP0 */
164 uint8_t o_gpgd_mask;
165
166 /* ALL STATUS RESPONSE value and flags */
167 bool all_status_updated;
168 uint16_t all_status_value;
169
170 /* OUTx fault registers */
171 uint16_t out_fault[2];
172 /* GP mode fault register */
173 /* TODO: check documentation if these faults also applied to GPx outputs in IGN mode */
174 uint16_t gp_fault;
175 /* IGN mode fault register */
176 uint16_t ign_fault;
177
178 uint16_t recentTx;
179
180 /* SPKDUR handling */
181 struct {
182 ioportid_t port;
183 uint_fast8_t pad;
184 } spkdur;
185 mc33810_coil_state coil_state;
186 uint8_t active_coil_idx; /* zero based, used as index of spark[] array */
187 uint8_t spark_fault_mask; /* 4 LSB bits are not used */
188 efitick_t spartStart[MC33810_IGN_OUTPUTS];
189 int spark_sync_err;
190
191 /* statistic */
192 int rst_cnt;
193 int cor_cnt;
194 int sor_cnt;
195 int ov_cnt;
196 int lv_cnt;
197
198 mc33810_drv_state drv_state;
199
200 bool hadSuccessfulInit = false;
201};
202
203static Mc33810 chips[BOARD_MC33810_COUNT];
204
205static const char* mc33810_pin_names[MC33810_OUTPUTS] = {
206 "mc33810.OUT1", "mc33810.OUT2", "mc33810.OUT3", "mc33810.OUT4",
207 "mc33810.GD0", "mc33810.GD1", "mc33810.GD2", "mc33810.GD3",
208};
209
210/*==========================================================================*/
211/* Driver local functions. */
212/*==========================================================================*/
213
214inline bool isCor(uint16_t rx) {
215 return rx & REP_FLAG_COR;
216}
217
218static void mc33810_spkdur_cb(void *ptr, efitick_t now);
219
220/**
221 * @brief MC33810 spi CS release helper with workaround
222 * @details Will wait until SCK = low before releasing CS
223 */
224
225int Mc33810::spi_unselect()
226{
227 int retry = 0;
228 SPIDriver *spi = cfg->spi_bus;
229
230 if (cfg->sck.port) {
231 /* Lets poll for SCK=0... spiPolledExchange() returns while SPI HW is
232 * still active and did not set SCK low yet. So do ot drive CS high until
233 * SCK is low. This polling should not take much time. But anyway we have
234 * timeout exit. */
235 while (palReadPad(cfg->sck.port, cfg->sck.pad) && (++retry < 1000)) {
236 /* NOP */
237 }
238 }
239
240 /* Slave Select de-assertion. */
241 spiUnselect(spi);
242
243 if (retry < 1000) {
244 return 0;
245 }
246
247 efiPrintf(DRIVER_NAME "failed wait for SCK = 0");
248
249 return -1;
250}
251
252
253/**
254 * @brief MC33810 send and receive routine.
255 * @details Sends and receives 16 bits. CS asserted before and released
256 * after transaction.
257 */
258
259int Mc33810::spi_rw(uint16_t tx, uint16_t *rx_ptr)
260{
261 uint16_t rx;
262 SPIDriver *spi = cfg->spi_bus;
263
264 /* Acquire ownership of the bus. */
265 spiAcquireBus(spi);
266 /* Setup transfer parameters. */
267 spiStart(spi, &cfg->spi_config);
268 /* Slave Select assertion. */
269 spiSelect(spi);
270 /* Atomic transfer operations. */
271 /* TODO: check why spiExchange transfers invalid data on STM32F7xx, DMA issue? */
272 //spiExchange(spi, 2, &tx, &rxb);
273 rx = spiPolledExchange(spi, tx);
274 /* Slave Select de-assertion. */
275 spi_unselect();
276 /* Ownership release. */
277 spiReleaseBus(spi);
278
279 if (rx_ptr)
280 *rx_ptr = rx;
281
282 if (recentTx != MC_CMD_INVALID) {
283 /* update statistic counters - common flags */
284 if (rx & REP_FLAG_RESET)
285 rst_cnt++;
286 if (isCor(rx))
287 cor_cnt++;
288
289 if (((TX_GET_CMD(recentTx) >= 0x1) && (TX_GET_CMD(recentTx) <= 0xa)) ||
290 (recentTx == MC_CMD_READ_REG(REG_ALL_STAT))) {
291 /* if reply on previous command is ALL STATUS RESPONSE */
292 all_status_value = rx;
293 all_status_updated = true;
294 /* update statistic counters - ALL STATUS flags */
295 if (rx & REP_FLAG_SOR)
296 sor_cnt++;
297 /* ignore NFM */
298 } else {
299 /* Some READ REGISTER reply with address != REG_ALL_STAT */
300 if (rx & REP_FLAG_OV)
301 ov_cnt++;
302 if (rx & REP_FLAG_LV)
303 lv_cnt++;
304 }
305 }
306
307 /* store currently tx'ed value to know what to expect on next rx */
308 recentTx = tx;
309#if 0
310 efiPrintf(DRIVER_NAME "SPI [%x][%x]", tx, rx);
311#endif
312 /* no errors for now */
313 return 0;
314}
315
316/**
317 * @return <0 in case of communication error or invalid argument
318 */
319int Mc33810::spi_rw_array(const uint16_t *tx, uint16_t *rx, int n)
320{
321 int ret = 0;
322 SPIDriver *spi = cfg->spi_bus;
323
324 if (n <= 0) {
325 return -2;
326 }
327
328 /* Acquire ownership of the bus. */
329 spiAcquireBus(spi);
330 /* Setup transfer parameters. */
331 spiStart(spi, &cfg->spi_config);
332
333 for (int i = 0; i < n; i++) {
334 /* Slave Select assertion. */
335 spiSelect(spi);
336 /* data transfer */
337 uint16_t rxdata = spiPolledExchange(spi, tx[i]);
338 if (rx)
339 rx[i] = rxdata;
340 /* Slave Select de-assertion. */
341 spi_unselect();
342
343 /* Parse reply */
344 if (recentTx != MC_CMD_INVALID) {
345 /* update statistic counters - common flags */
346 if (rxdata & REP_FLAG_RESET)
347 rst_cnt++;
348 if (isCor(rxdata))
349 cor_cnt++;
350
351 if (((TX_GET_CMD(recentTx) >= 0x1) && (TX_GET_CMD(recentTx) <= 0xa)) ||
352 (recentTx == MC_CMD_READ_REG(REG_ALL_STAT))) {
353 /* if reply on previous command is ALL STATUS RESPONSE */
354 all_status_value = rxdata;
355 all_status_updated = true;
356 /* update statistic counters - ALL STATUS flags */
357 if (rxdata & REP_FLAG_SOR)
358 sor_cnt++;
359 /* ignore NFM */
360 } else {
361 /* Some READ REGISTER reply with address != REG_ALL_STAT */
362 if (rxdata & REP_FLAG_OV)
363 ov_cnt++;
364 if (rxdata & REP_FLAG_LV)
365 lv_cnt++;
366 }
367 }
368
369 /* store currently tx'ed value to know what to expect on next rx */
370 recentTx = tx[i];
371
372 if (ret < 0) {
373 recentTx = MC_CMD_INVALID;
374 break;
375 }
376 }
377
378 /* Ownership release. */
379 spiReleaseBus(spi);
380
381 /* no errors for now */
382 return ret;
383}
384
385/**
386 * @brief MC33810 send output state data.
387 * @details Sends ORed data to register, also receive diagnostic.
388 */
389
390int Mc33810::update_output_and_diag()
391{
392 int ret = 0;
393
394 uint16_t out_data = o_state & (~o_direct_mask);
395 const uint16_t tx[] = {
396 // we will get ALL STATUS RESPONSE as reply on following commad
397 // value will be stored inside spi_rw_array() call, no need to care
398 // TODO: WTF?
399 (uint16_t)MC_CMD_DRIVER_EN(out_data),
400 MC_CMD_READ_REG(REG_OUT10_FAULT),
401 MC_CMD_READ_REG(REG_OUT32_FAULT),
402 MC_CMD_READ_REG(REG_GPGD_FAULT),
403 MC_CMD_READ_REG(REG_IGN_FAULT),
404 MC_CMD_READ_REG(REG_ALL_STAT)
405 };
406 uint16_t rx[efi::size(tx)];
407
408 /* we need to get updated status */
409 all_status_updated = false;
410
411 ret = spi_rw_array(tx, rx, efi::size(tx));
412
413 if (ret == 0) {
414 /* the content of the requested register is transmitted with the
415 * next SPI transmission */
416
417 /* TODO: lock? */
418 out_fault[0] = rx[1 + 1];
419 out_fault[1] = rx[2 + 1];
420 gp_fault = rx[3 + 1];
421 ign_fault = rx[4 + 1];
422
423 alive_cnt++;
424 /* TODO: unlock? */
425 }
426
427 return ret;
428}
429
430int Mc33810::chip_init_data()
431{
432 int ret;
433
434 /* mark pins used */
435 //ret = gpio_pin_markUsed(cfg->spi_config.ssport, cfg->spi_config.sspad, DRIVER_NAME " CS");
436 ret = 0;
437 if (cfg->en.port) {
438 ret |= gpio_pin_markUsed(cfg->en.port, cfg->en.pad, DRIVER_NAME " EN");
439 palSetPadMode(cfg->en.port, cfg->en.pad, PAL_MODE_OUTPUT_PUSHPULL);
440 palSetPort(cfg->en.port, PAL_PORT_BIT(cfg->en.pad));
441 }
442
443 for (int n = 0; n < MC33810_DIRECT_OUTPUTS; n++) {
444 if (cfg->direct_io[n].port) {
445 ret |= gpio_pin_markUsed(cfg->direct_io[n].port, cfg->direct_io[n].pad, DRIVER_NAME " DIRECT IO");
446 palSetPadMode(cfg->direct_io[n].port, cfg->direct_io[n].pad, PAL_MODE_OUTPUT_PUSHPULL);
447 palClearPort(cfg->direct_io[n].port, PAL_PORT_BIT(cfg->direct_io[n].pad));
448 }
449 }
450
451 if (ret) {
452 ret = -6;
453 efiPrintf(DRIVER_NAME " error binding pin(s)");
454 goto err_gpios;
455 }
456
457 /* check if we support SPKDUR */
458 if (isBrainPinValid(cfg->spkdur) && brain_pin_is_onchip(cfg->spkdur)) {
459 ret = efiExtiEnablePin(DRIVER_NAME "SPKDUR", cfg->spkdur, PAL_EVENT_MODE_BOTH_EDGES, mc33810_spkdur_cb,
460 reinterpret_cast<void*>(this));
461 if (ret) {
462 efiPrintf(DRIVER_NAME " error requesting SPKDUR input IRQ: %d", ret);
463 // This is not critical
464 ret = 0;
465 goto exit;
466 }
467 spkdur.port = getHwPort(DRIVER_NAME, cfg->spkdur);
468 spkdur.pad = getHwPin(DRIVER_NAME, cfg->spkdur);
469 } else {
470 spkdur.port = nullptr;
471 }
472
473 return 0;
474
475err_gpios:
476 /* unmark pins */
477 //gpio_pin_markUnused(cfg->spi_config.ssport, cfg->spi_config.sspad);
478#if SMART_CHIPS_UNMARK_ON_FAIL
479 if (cfg->en.port) {
480 /* disable and mark unused */
481 palSetPort(cfg->en.port,
482 PAL_PORT_BIT(cfg->en.pad));
483 gpio_pin_markUnused(cfg->en.port, cfg->en.pad);
484 }
485
486 for (int n = 0; n < MC33810_DIRECT_OUTPUTS; n++) {
487 if (cfg->direct_io[n].port) {
488 gpio_pin_markUnused(cfg->direct_io[n].port, cfg->direct_io[n].pad);
489 }
490 }
491#endif
492
493exit:
494 return ret;
495}
496
497/**
498 * @brief MC33810 chip init.
499 * @details Checks communication. Check chip presence.
500 */
501
502int Mc33810::chip_init()
503{
504 int ret;
505 uint16_t rx;
506 uint16_t rxSpiCheck;
507
508// duplication with mc33810spiErrorCounter?
509 init_cnt++;
510
511 /* we do not know last CMD was sent (if was) */
512 recentTx = MC_CMD_INVALID;
513
514 /* check SPI communication */
515 /* 0. set echo mode, chip number - don't care,
516 * NOTE: chip replyes on NEXT spi transaction */
517 ret = spi_rw(MC_CMD_SPI_CHECK, &rxSpiCheck);
518 /* 1. check loopback */
519 ret |= spi_rw(MC_CMD_READ_REG(REG_REV), &rx);
520 if (ret) {
521 ret = -7;
522 efiPrintf(DRIVER_NAME " first SPI RX failed");
523 goto err_exit;
524 }
525 if (rx != SPI_CHECK_ACK) {
527 static Timer needBatteryMessage;
529 if (vBatt > 6 || needBatteryMessage.getElapsedSeconds() > 7) {
530 needBatteryMessage.reset();
531 const char *msg;
532 if (rx == 0xffff) {
533 msg = "No power?";
534 } else if (isCor(rx)) {
535 msg = "COR";
536 } else {
537 msg = "unexpected";
538 }
539 efiPrintf(DRIVER_NAME " spi loopback test failed [first 0x%04x][spi check 0x%04x][%s] vBatt=%f count=%d", rxSpiCheck, rx, msg, vBatt,
541 }
542 ret = -2;
543 goto err_exit;
544 }
545
546 /* 2. read revision */
547 ret = spi_rw(MC_CMD_READ_REG(REG_ALL_STAT), &rx);
548 if (ret) {
549 ret = -8;
550 efiPrintf(DRIVER_NAME " revision failed");
551 goto err_exit;
552 }
553 if (isCor(rx)) {
554 efiPrintf(DRIVER_NAME " spi COR status");
555 ret = -3;
556 goto err_exit;
557 }
558
559 /* TODO:
560 * - setup
561 * - enable output drivers
562 * - read diagnostic
563 */
564
565 {
566 uint16_t spark_cmd =
567 // Table 11. Maximum Dwell Timer
569 BIT(8) | /* enable max dwell control */
570 (3 << 2) | /* Open Secondary OSFLT = 100 uS, default */
571 (1 << 0) | /* End Spark THreshold: VPWR +5.5V, default */
572 0;
573 ret = spi_rw(MC_CMD_SPARK(spark_cmd), NULL);
574 if (ret) {
575 efiPrintf(DRIVER_NAME " cmd spark");
576 goto err_exit;
577 }
578
579 uint16_t nomi_current = 0x0a; // default = 5.5 A
580 float nomi = engineConfiguration->mc33810Nomi;
581 if ((nomi >= 3.0) && (nomi <= 10.75)) {
582 nomi_current = (nomi - 3.0) / 0.25;
583 }
584
585 uint16_t maxi_current = 0x08; // default = 14.0 A
586 float maxi = engineConfiguration->mc33810Maxi;
587 if ((maxi >= 6.0) && (maxi <= 21.0)) {
588 maxi_current = maxi - 6.0;
589 }
590 uint16_t dac_cmd =
591 // Table 12. Nominal Current DAC Select
592 ((nomi_current & 0x1f) << 0) |
593 // Table 10. Overlapping Dwell Compensation, defaul 35%
594 (0x4 << 5) |
595 // Table 13. Maximum Current DAC Select
596 ((maxi_current & 0xf) << 8) |
597 0;
598 ret = spi_rw(MC_CMD_DAC(dac_cmd), NULL);
599 if (ret) {
600 efiPrintf(DRIVER_NAME " cmd dac");
601 goto err_exit;
602 }
603
604 /* update local configuration mask */
605 o_gpgd_mask =
610
611 uint16_t mode_select_cmd =
612 /* set IGN/GP mode for GPx outputs: [7:4] to [11:8] */
613 ((o_gpgd_mask & 0xf0) << 4) |
614 /* disable/enable retry after recovering from under/overvoltage */
616 0;
617 ret = spi_rw(MC_CMD_MODE_SELECT(mode_select_cmd) , NULL);
618 if (ret) {
619 efiPrintf(DRIVER_NAME " cmd mode select");
620 goto err_exit;
621 }
622 }
623
624 /* n. set EN pin low - active */
625 if (cfg->en.port) {
626 palClearPort(cfg->en.port,
627 PAL_PORT_BIT(cfg->en.pad));
628 }
629
630 if (!hadSuccessfulInit) {
631 efiPrintf(DRIVER_NAME " Successful Init");
632 hadSuccessfulInit = true;
633 }
634
635 return 0;
636
637err_exit:
638 return ret;
639}
640
641/**
642 * @brief MC33810 chip driver wakeup.
643 * @details Wake up driver. Will cause output register and
644 * diagnostic update.
645 */
646
647void Mc33810::wake_driver()
648{
649 /* Entering a reentrant critical zone.*/
650 chibios_rt::CriticalSectionLocker csl;
651 chSemSignalI(&mc33810_wake);
652 if (!port_is_isr_context()) {
653 /**
654 * chSemSignalI above requires rescheduling
655 * interrupt handlers have implicit rescheduling
656 */
657 chSchRescheduleS();
658 }
659}
660
661/**
662 * @brief MC33810 SPKDUR event hook.
663 * @details Called on falling and rising edges of SPKDUR input.
664 */
665
666void Mc33810::on_spkdur(efitick_t now)
667{
668 if (coil_state == COIL_IDLE) {
669 /* ignore spurious events */
670 return;
671 }
672
673 bool edge = palReadPad(spkdur.port, spkdur.pad);
674
675 /* signal is active low */
676 if ((!edge) && (coil_state == COIL_WAIT_SPARK_START)) {
677 /* expected falling edge */
678 spartStart[active_coil_idx] = now;
679 coil_state = COIL_WAIT_SPARK_END;
680 } else if ((edge) && (coil_state == COIL_WAIT_SPARK_END)) {
681 /* expected rise edge */
682 sparkDuration[active_coil_idx] = USF2MS(NT2USF(now - spartStart[active_coil_idx]));
683 /* clear fault flag */
684 spark_fault_mask &= ~BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
685 coil_state = COIL_IDLE;
686 } else {
687 /* unexpected event */
688 spark_sync_err++;
689 sparkDuration[active_coil_idx] = 0;
690 spark_fault_mask |= BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
691 coil_state = COIL_IDLE;
692 }
693}
694
695/**
696 * @brief MC33810 ignition inputs event handler.
697 * @details Called right before ignition input (GIN0..GIN3) changes its state.
698 */
699
700void Mc33810::ign_event(size_t pin, int value)
701{
702 /* SPKDUR not routed to MCU */
703 if (spkdur.port == nullptr) {
704 return;
705 }
706
707 uint8_t pin_mask = BIT(pin);
708 uint8_t new_o_state = o_state;
709
710 if (value) {
711 new_o_state |= pin_mask;
712 } else {
713 new_o_state &= ~pin_mask;
714 }
715
716 /* nothing's going change */
717 if (o_state == new_o_state)
718 return;
719
720 if (value) {
721 /* coil charge starting */
722 /* nothing to do here, we can still wait SPKDUR event from another coil */
723 } else {
724 size_t idx = pin - MC33810_INJ_OUTPUTS;
725 /* coil firing */
726 /* if we did not get some event for previously fired coil... */
727 if (coil_state != COIL_IDLE) {
728 /* ...mark this coil as failed */
729 spark_fault_mask |= BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
730 sparkDuration[active_coil_idx] = 0;
731 }
732
733 active_coil_idx = idx;
734 coil_state = COIL_WAIT_SPARK_START;
735 }
736}
737
738/*==========================================================================*/
739/* Driver thread. */
740/*==========================================================================*/
741
742static THD_FUNCTION(mc33810_driver_thread, p) {
743 (void)p;
744
745 chRegSetThreadName(DRIVER_NAME);
746
747 chThdSleepMilliseconds(2); // let's wait BatteryVoltage to appear. TODO: more proper way of synchronization with BatteryVoltage!
748
749
750 while (true) {
751 msg_t msg = chSemWaitTimeout(&mc33810_wake, TIME_MS2I(MC33810_POLL_INTERVAL_MS));
752
753 /* should we care about msg == MSG_TIMEOUT? */
754 (void)msg;
755
756 for (int i = 0; i < BOARD_MC33810_COUNT; i++) {
757 auto chip = &chips[i];
758
759 if (i == 0) {
761 engine->engineState.smartChipAliveCounter = chip->alive_cnt;
762 }
763
764 if (chip->need_init) {
765 int ret = chip->chip_init();
766 if (ret == 0) {
767 chip->drv_state = MC33810_READY;
768 chip->need_init = false;
769 }
770 }
771
772 if ((chip->cfg == NULL) ||
773 (chip->drv_state == MC33810_DISABLED) ||
774 (chip->drv_state == MC33810_FAILED)) {
775 chip->need_init = true;
776 continue;
777 }
778
779 /* TODO: implement indirect driven gpios */
780 int ret = chip->update_output_and_diag();
781 if (ret) {
782 /* set state to MC33810_FAILED? */
783 }
784 }
785 }
786}
787
788/*==========================================================================*/
789/* Driver interrupt handlers. */
790/*==========================================================================*/
791
792static void mc33810_spkdur_cb(void *ptr, efitick_t now)
793{
794 Mc33810 *chip = (Mc33810 *)ptr;
795
796 chip->on_spkdur(now);
797}
798
799/*==========================================================================*/
800/* Driver exported functions. */
801/*==========================================================================*/
802
803int Mc33810::writePad(size_t pin, int value) {
804 uint8_t pin_mask = BIT(pin);
805
806 if (pin >= MC33810_OUTPUTS) {
807 return -12;
808 }
809
810 {
811 // mutate driver state under lock
812 chibios_rt::CriticalSectionLocker csl;
813
814 if (pin_mask & IGN_MASK) {
815 ign_event(pin, value);
816 }
817
818 if (value) {
819 o_state |= pin_mask;
820 } else {
821 o_state &= ~pin_mask;
822 }
823 }
824
825 /* direct driven? */
826 if (o_direct_mask & pin_mask) {
827 /* TODO: ensure that output driver enabled */
828#if MC33810_VERBOSE
829 int pad = PAL_PORT_BIT(cfg->direct_io[pin].pad);
830 efiPrintf(DRIVER_NAME "writePad pad %d", pad);
831#endif
832 if (value) {
833 palSetPort(cfg->direct_io[pin].port,
834 PAL_PORT_BIT(cfg->direct_io[pin].pad));
835 } else {
836 palClearPort(cfg->direct_io[pin].port,
837 PAL_PORT_BIT(cfg->direct_io[pin].pad));
838 }
839 } else {
840 wake_driver();
841 }
842
843 return 0;
844}
845
846brain_pin_diag_e Mc33810::getDiag(size_t pin)
847{
848 uint16_t val;
849 int diag = PIN_OK;
850
851 if (pin >= MC33810_DIRECT_OUTPUTS)
852 return PIN_UNKNOWN;
853
854 if (pin < MC33810_INJ_OUTPUTS) {
855 /* OUT drivers */
856 val = out_fault[(pin < 2) ? 0 : 1] >> (4 * (pin & 0x01));
857
858 /* ON open fault */
859 if (val & BIT(0))
860 diag |= PIN_OPEN;
861 /* OFF open fault */
862 if (val & BIT(1))
863 diag |= PIN_OPEN;
864 if (val & BIT(2))
865 diag |= PIN_SHORT_TO_BAT;
866 if (val & BIT(3))
867 diag |= PIN_DRIVER_OVERTEMP;
868 } else {
869 /* Commom GP faults */
870 val = gp_fault >> (2 * (pin - 4));
871
872 if (val & BIT(0))
873 diag |= PIN_OPEN;
874 if (val & BIT(1))
875 diag |= PIN_SHORT_TO_GND;
876
877 /* IGN mode faults, only if not in GPGD mode */
878 if ((o_gpgd_mask & BIT(pin)) == 0) {
879 val = ign_fault >> (3 * (pin - 4));
880
881 /* open load */
882 if (val & BIT(0))
883 diag |= PIN_OPEN;
884 /* max Dwell fault - too long coil charge time */
885 if (val & BIT(1))
886 diag |= PIN_OVERLOAD;
887 /* MAXI fault - too high coil current */
888 if (val & BIT(2))
889 diag |= PIN_OVERLOAD;
890
891 /* no SPKDUR detected */
892 if (spark_fault_mask & BIT(pin))
893 diag |= PIN_OPEN;
894
895 /* too short spark time means there is oscilation on coil,
896 * that usualy because of open secondary (disconnected spark plug) */
897 if (sparkDuration[pin - MC33810_IGN_OUTPUTS] < 0.150)
898 diag |= PIN_OPEN;
899 }
900 }
901 /* convert to some common enum? */
902 return static_cast<brain_pin_diag_e>(diag);
903}
904
905void Mc33810::debug() {
906 efiPrintf("rst_cnt %d cor_cnt %d sor_cnt %d ov_cnt %d lv_cnt %d\n",
907 rst_cnt, cor_cnt, sor_cnt, ov_cnt, lv_cnt);
908
909 for (size_t i = 0; i < MC33810_IGN_OUTPUTS; i++) {
910 efiPrintf("Ign %d spark fault %d last duration %f mS\n",
911 i, !!(spark_fault_mask & BIT(MC33810_INJ_OUTPUTS + i)),
912 sparkDuration[i]);
913 }
914}
915
916int Mc33810::init() {
917 int ret;
918
919 /* check for multiple init */
920 if (drv_state != MC33810_WAIT_INIT)
921 return -1;
922
923 ret = chip_init_data();
924 if (ret)
925 return ret;
926
927 /* force init from driver thread */
928 need_init = true;
929
930 /* instance is ready */
931 drv_state = MC33810_READY;
932
933 if (!mc33810_thread) {
934 mc33810_thread = chThdCreateStatic(mc33810_thread_wa, sizeof(mc33810_thread_wa),
935 PRIO_GPIOCHIP, mc33810_driver_thread, nullptr);
936 }
937
938 return 0;
939}
940
941/**
942 * @brief MC33810 driver add.
943 * @details Checks for valid config
944 */
945
946int mc33810_add(brain_pin_e base, unsigned int index, const mc33810_config *cfg) {
947
948 /* no config or no such chip */
949 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_MC33810_COUNT))
950 return -16;
951
952 /* check for valid cs.
953 * TODO: remove this check? CS can be driven by SPI */
954 //if (cfg->spi_config.ssport == NULL)
955 // return -1;
956
957 Mc33810& chip = chips[index];
958
959 /* already initted? */
960 if (chip.cfg != NULL)
961 return -13;
962
963 chip.cfg = cfg;
964 chip.o_state = 0;
965 chip.o_state_cached = 0;
966 chip.o_direct_mask = 0;
967 for (int i = 0; i < MC33810_DIRECT_OUTPUTS; i++) {
968 if (cfg->direct_io[i].port != 0)
969 chip.o_direct_mask |= BIT(i);
970 }
971
972 /* GPGD mode is not supported yet, ignition mode does not support spi on/off commands
973 * so ignition signals should be directly driven */
974 if ((chip.o_direct_mask & 0xf0) != 0xf0)
975 return -4;
976
977 /* register, return gpio chip base */
978 int ret = gpiochip_register(base, DRIVER_NAME, chip, MC33810_OUTPUTS);
979 if (ret < 0)
980 return ret;
981
982 /* set default pin names, board init code can rewrite */
984
985 chip.drv_state = MC33810_WAIT_INIT;
986
987 return ret;
988}
989
990/*==========================================================================*/
991/* Driver exported debug functions. */
992/*==========================================================================*/
994 size_t i;
995
996 for (i = 0; i < BOARD_MC33810_COUNT; i++) {
997 auto& chip = chips[i];
998
999 chip.need_init = true;
1000 }
1001}
1002
1004switch(value) {
1005case DWELL_16MS:
1006 return 16;
1007case DWELL_2MS:
1008 return 2;
1009case DWELL_32MS:
1010 return 32;
1011case DWELL_4MS:
1012 return 4;
1013case DWELL_64MS:
1014 return 64;
1015case DWELL_8MS:
1016 return 8;
1017 }
1018 return 0;
1019}
1020
1022 if (idx >= BOARD_MC33810_COUNT)
1023 return nullptr;
1024 return &chips[idx];
1025}
1026
1027#else /* BOARD_MC33810_COUNT > 0 */
1028
1029int mc33810_add(brain_pin_e base, unsigned int index, const mc33810_config *cfg)
1030{
1031 (void)base; (void)index; (void)cfg;
1032
1033 return -5;
1034}
1035
1036const mc33810_state_s* mc33810getLiveData(size_t) {
1037 return nullptr;
1038}
1039
1040#endif /* BOARD_MC33810_COUNT */
uint8_t pad[3]
EngineState engineState
Definition engine.h:344
TunerStudioOutputChannels outputChannels
Definition engine.h:109
static float getOrZero(SensorType type)
Definition sensor.h:83
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
int efiExtiEnablePin(const char *msg, brain_pin_e brainPin, uint32_t mode, ExtiCallback cb, void *cb_data)
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
GPIO_TypeDef * ioportid_t
Port Identifier.
static void mc33810_spkdur_cb(void *ptr, efitick_t now)
Definition mc33810.cpp:792
static Mc33810 chips[BOARD_MC33810_COUNT]
Definition mc33810.cpp:203
mc33810_coil_state
Definition mc33810.cpp:51
@ COIL_IDLE
Definition mc33810.cpp:52
@ COIL_WAIT_SPARK_END
Definition mc33810.cpp:54
@ COIL_WAIT_SPARK_START
Definition mc33810.cpp:53
static THD_FUNCTION(mc33810_driver_thread, p)
Definition mc33810.cpp:742
static const char * mc33810_pin_names[MC33810_OUTPUTS]
Definition mc33810.cpp:205
int getMc33810maxDwellTimer(mc33810maxDwellTimer_e value)
Definition mc33810.cpp:1003
void mc33810_req_init()
Definition mc33810.cpp:993
const mc33810_state_s * mc33810getLiveData(size_t idx)
Definition mc33810.cpp:1021
bool isCor(uint16_t rx)
Definition mc33810.cpp:214
static THD_WORKING_AREA(mc33810_thread_wa, 256)
mc33810_drv_state
Definition mc33810.cpp:44
@ MC33810_WAIT_INIT
Definition mc33810.cpp:46
@ MC33810_FAILED
Definition mc33810.cpp:48
@ MC33810_READY
Definition mc33810.cpp:47
@ MC33810_DISABLED
Definition mc33810.cpp:45
int mc33810_add(brain_pin_e base, unsigned int index, const mc33810_config *cfg)
MC33810 driver add.
Definition mc33810.cpp:946
static thread_t * mc33810_thread
Definition mc33810.cpp:126
SEMAPHORE_DECL(mc33810_wake, 10)
void gpio_pin_markUnused(ioportid_t port, ioportmask_t pin)
bool brain_pin_is_onchip(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
bool gpio_pin_markUsed(ioportid_t port, ioportmask_t pin, const char *msg)
mc33810maxDwellTimer_e
brain_pin_diag_e
brain_pin_e pin
Definition stm32_adc.cpp:15
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 init()=0
virtual void debug()
Definition gpio_ext.h:33
ioportid_t port
Definition mc33810.h:35
struct mc33810_config::@36 direct_io[MC33810_DIRECT_OUTPUTS]
SPIDriver * spi_bus
Definition mc33810.h:30