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 // TODO: add check of o_direct_mask against o_gpgd_mask
612 // Check is some of IGN mode channels have no direct drive gpio assigned
613
614 uint16_t mode_select_cmd =
615 /* set IGN/GP mode for GPx outputs: [7:4] to [11:8] */
616 ((o_gpgd_mask & 0xf0) << 4) |
617 /* disable/enable retry after recovering from under/overvoltage */
619 0;
620 ret = spi_rw(MC_CMD_MODE_SELECT(mode_select_cmd) , NULL);
621 if (ret) {
622 efiPrintf(DRIVER_NAME " cmd mode select");
623 goto err_exit;
624 }
625 }
626
627 /* n. set EN pin low - active */
628 if (cfg->en.port) {
629 palClearPort(cfg->en.port,
630 PAL_PORT_BIT(cfg->en.pad));
631 }
632
633 if (!hadSuccessfulInit) {
634 efiPrintf(DRIVER_NAME " Successful Init");
635 hadSuccessfulInit = true;
636 }
637
638 return 0;
639
640err_exit:
641 return ret;
642}
643
644/**
645 * @brief MC33810 chip driver wakeup.
646 * @details Wake up driver. Will cause output register and
647 * diagnostic update.
648 */
649
650void Mc33810::wake_driver()
651{
652 /* Entering a reentrant critical zone.*/
653 chibios_rt::CriticalSectionLocker csl;
654 chSemSignalI(&mc33810_wake);
655 if (!port_is_isr_context()) {
656 /**
657 * chSemSignalI above requires rescheduling
658 * interrupt handlers have implicit rescheduling
659 */
660 chSchRescheduleS();
661 }
662}
663
664/**
665 * @brief MC33810 SPKDUR event hook.
666 * @details Called on falling and rising edges of SPKDUR input.
667 */
668
669void Mc33810::on_spkdur(efitick_t now)
670{
671 if (coil_state == COIL_IDLE) {
672 /* ignore spurious events */
673 return;
674 }
675
676 bool edge = palReadPad(spkdur.port, spkdur.pad);
677
678 /* signal is active low */
679 if ((!edge) && (coil_state == COIL_WAIT_SPARK_START)) {
680 /* expected falling edge */
681 spartStart[active_coil_idx] = now;
682 coil_state = COIL_WAIT_SPARK_END;
683 } else if ((edge) && (coil_state == COIL_WAIT_SPARK_END)) {
684 /* expected rise edge */
685 sparkDuration[active_coil_idx] = USF2MS(NT2USF(now - spartStart[active_coil_idx]));
686 /* clear fault flag */
687 spark_fault_mask &= ~BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
688 coil_state = COIL_IDLE;
689 } else {
690 /* unexpected event */
691 spark_sync_err++;
692 sparkDuration[active_coil_idx] = 0;
693 spark_fault_mask |= BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
694 coil_state = COIL_IDLE;
695 }
696}
697
698/**
699 * @brief MC33810 ignition inputs event handler.
700 * @details Called right before ignition input (GIN0..GIN3) changes its state.
701 */
702
703void Mc33810::ign_event(size_t pin, int value)
704{
705 /* SPKDUR not routed to MCU */
706 if (spkdur.port == nullptr) {
707 return;
708 }
709
710 uint8_t pin_mask = BIT(pin);
711 uint8_t new_o_state = o_state;
712
713 if (value) {
714 new_o_state |= pin_mask;
715 } else {
716 new_o_state &= ~pin_mask;
717 }
718
719 /* nothing's going change */
720 if (o_state == new_o_state)
721 return;
722
723 if (value) {
724 /* coil charge starting */
725 /* nothing to do here, we can still wait SPKDUR event from another coil */
726 } else {
727 size_t idx = pin - MC33810_INJ_OUTPUTS;
728 /* coil firing */
729 /* if we did not get some event for previously fired coil... */
730 if (coil_state != COIL_IDLE) {
731 /* ...mark this coil as failed */
732 spark_fault_mask |= BIT(MC33810_INJ_OUTPUTS + active_coil_idx);
733 sparkDuration[active_coil_idx] = 0;
734 }
735
736 active_coil_idx = idx;
737 coil_state = COIL_WAIT_SPARK_START;
738 }
739}
740
741/*==========================================================================*/
742/* Driver thread. */
743/*==========================================================================*/
744
745static THD_FUNCTION(mc33810_driver_thread, p) {
746 (void)p;
747
748 chRegSetThreadName(DRIVER_NAME);
749
750 chThdSleepMilliseconds(2); // let's wait BatteryVoltage to appear. TODO: more proper way of synchronization with BatteryVoltage!
751
752
753 while (true) {
754 msg_t msg = chSemWaitTimeout(&mc33810_wake, TIME_MS2I(MC33810_POLL_INTERVAL_MS));
755
756 /* should we care about msg == MSG_TIMEOUT? */
757 (void)msg;
758
759 for (int i = 0; i < BOARD_MC33810_COUNT; i++) {
760 auto chip = &chips[i];
761
762 if (i == 0) {
764 engine->engineState.smartChipAliveCounter = chip->alive_cnt;
765 }
766
767 if (chip->need_init) {
768 int ret = chip->chip_init();
769 if (ret == 0) {
770 chip->drv_state = MC33810_READY;
771 chip->need_init = false;
772 }
773 }
774
775 if ((chip->cfg == NULL) ||
776 (chip->drv_state == MC33810_DISABLED) ||
777 (chip->drv_state == MC33810_FAILED)) {
778 chip->need_init = true;
779 continue;
780 }
781
782 /* TODO: implement indirect driven gpios */
783 int ret = chip->update_output_and_diag();
784 if (ret) {
785 /* set state to MC33810_FAILED? */
786 }
787 }
788 }
789}
790
791/*==========================================================================*/
792/* Driver interrupt handlers. */
793/*==========================================================================*/
794
795static void mc33810_spkdur_cb(void *ptr, efitick_t now)
796{
797 Mc33810 *chip = (Mc33810 *)ptr;
798
799 chip->on_spkdur(now);
800}
801
802/*==========================================================================*/
803/* Driver exported functions. */
804/*==========================================================================*/
805
806int Mc33810::writePad(size_t pin, int value) {
807 uint8_t pin_mask = BIT(pin);
808
809 if (pin >= MC33810_OUTPUTS) {
810 return -12;
811 }
812
813 {
814 // mutate driver state under lock
815 chibios_rt::CriticalSectionLocker csl;
816
817 if (pin_mask & IGN_MASK) {
818 ign_event(pin, value);
819 }
820
821 if (value) {
822 o_state |= pin_mask;
823 } else {
824 o_state &= ~pin_mask;
825 }
826 }
827
828 /* direct driven? */
829 if (o_direct_mask & pin_mask) {
830 /* TODO: ensure that output driver enabled */
831#if MC33810_VERBOSE
832 int pad = PAL_PORT_BIT(cfg->direct_io[pin].pad);
833 efiPrintf(DRIVER_NAME "writePad pad %d", pad);
834#endif
835 if (value) {
836 palSetPort(cfg->direct_io[pin].port,
837 PAL_PORT_BIT(cfg->direct_io[pin].pad));
838 } else {
839 palClearPort(cfg->direct_io[pin].port,
840 PAL_PORT_BIT(cfg->direct_io[pin].pad));
841 }
842 } else {
843 wake_driver();
844 }
845
846 return 0;
847}
848
849brain_pin_diag_e Mc33810::getDiag(size_t pin)
850{
851 uint16_t val;
852 int diag = PIN_OK;
853
854 if (pin >= MC33810_DIRECT_OUTPUTS)
855 return PIN_UNKNOWN;
856
857 if (pin < MC33810_INJ_OUTPUTS) {
858 /* OUT drivers */
859 val = out_fault[(pin < 2) ? 0 : 1] >> (4 * (pin & 0x01));
860
861 /* ON open fault */
862 if (val & BIT(0))
863 diag |= PIN_OPEN;
864 /* OFF open fault */
865 if (val & BIT(1))
866 diag |= PIN_OPEN;
867 if (val & BIT(2))
868 diag |= PIN_SHORT_TO_BAT;
869 if (val & BIT(3))
870 diag |= PIN_DRIVER_OVERTEMP;
871 } else {
872 /* Commom GP faults */
873 val = gp_fault >> (2 * (pin - 4));
874
875 if (val & BIT(0))
876 diag |= PIN_OPEN;
877 if (val & BIT(1))
878 diag |= PIN_SHORT_TO_GND;
879
880 /* IGN mode faults, only if not in GPGD mode */
881 if ((o_gpgd_mask & BIT(pin)) == 0) {
882 val = ign_fault >> (3 * (pin - 4));
883
884 /* open load */
885 if (val & BIT(0))
886 diag |= PIN_OPEN;
887 /* max Dwell fault - too long coil charge time */
888 if (val & BIT(1))
889 diag |= PIN_OVERLOAD;
890 /* MAXI fault - too high coil current */
891 if (val & BIT(2))
892 diag |= PIN_OVERLOAD;
893
894 /* no SPKDUR detected */
895 if (spark_fault_mask & BIT(pin))
896 diag |= PIN_OPEN;
897
898 /* too short spark time means there is oscilation on coil,
899 * that usualy because of open secondary (disconnected spark plug) */
900 if (sparkDuration[pin - MC33810_IGN_OUTPUTS] < 0.150)
901 diag |= PIN_OPEN;
902 }
903 }
904 /* convert to some common enum? */
905 return static_cast<brain_pin_diag_e>(diag);
906}
907
908void Mc33810::debug() {
909 efiPrintf("rst_cnt %d cor_cnt %d sor_cnt %d ov_cnt %d lv_cnt %d\n",
910 rst_cnt, cor_cnt, sor_cnt, ov_cnt, lv_cnt);
911
912 for (size_t i = 0; i < MC33810_IGN_OUTPUTS; i++) {
913 efiPrintf("Ign %d spark fault %d last duration %f mS\n",
914 i, !!(spark_fault_mask & BIT(MC33810_INJ_OUTPUTS + i)),
915 sparkDuration[i]);
916 }
917}
918
919int Mc33810::init() {
920 int ret;
921
922 /* check for multiple init */
923 if (drv_state != MC33810_WAIT_INIT)
924 return -1;
925
926 ret = chip_init_data();
927 if (ret)
928 return ret;
929
930 /* force init from driver thread */
931 need_init = true;
932
933 /* instance is ready */
934 drv_state = MC33810_READY;
935
936 if (!mc33810_thread) {
937 mc33810_thread = chThdCreateStatic(mc33810_thread_wa, sizeof(mc33810_thread_wa),
938 PRIO_GPIOCHIP, mc33810_driver_thread, nullptr);
939 }
940
941 return 0;
942}
943
944/**
945 * @brief MC33810 driver add.
946 * @details Checks for valid config
947 */
948
949int mc33810_add(brain_pin_e base, unsigned int index, const mc33810_config *cfg) {
950
951 /* no config or no such chip */
952 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_MC33810_COUNT))
953 return -16;
954
955 /* check for valid cs.
956 * TODO: remove this check? CS can be driven by SPI */
957 //if (cfg->spi_config.ssport == NULL)
958 // return -1;
959
960 Mc33810& chip = chips[index];
961
962 /* already initted? */
963 if (chip.cfg != NULL)
964 return -13;
965
966 chip.cfg = cfg;
967 chip.o_state = 0;
968 chip.o_state_cached = 0;
969 chip.o_direct_mask = 0;
970 for (int i = 0; i < MC33810_DIRECT_OUTPUTS; i++) {
971 if (cfg->direct_io[i].port != 0)
972 chip.o_direct_mask |= BIT(i);
973 }
974
975#if 0
976 // Some Ignition channels may be unused, so skip this check for now
977 /* GPGD mode is not supported yet, ignition mode does not support spi on/off commands
978 * so ignition signals should be directly driven */
979 if ((chip.o_direct_mask & 0xf0) != 0xf0)
980 return -4;
981#endif
982
983 /* register, return gpio chip base */
984 int ret = gpiochip_register(base, DRIVER_NAME, chip, MC33810_OUTPUTS);
985 if (ret < 0)
986 return ret;
987
988 /* set default pin names, board init code can rewrite */
990
991 chip.drv_state = MC33810_WAIT_INIT;
992
993 return ret;
994}
995
996/*==========================================================================*/
997/* Driver exported debug functions. */
998/*==========================================================================*/
1000 size_t i;
1001
1002 for (i = 0; i < BOARD_MC33810_COUNT; i++) {
1003 auto& chip = chips[i];
1004
1005 chip.need_init = true;
1006 }
1007}
1008
1010switch(value) {
1011case DWELL_16MS:
1012 return 16;
1013case DWELL_2MS:
1014 return 2;
1015case DWELL_32MS:
1016 return 32;
1017case DWELL_4MS:
1018 return 4;
1019case DWELL_64MS:
1020 return 64;
1021case DWELL_8MS:
1022 return 8;
1023 }
1024 return 0;
1025}
1026
1028 if (idx >= BOARD_MC33810_COUNT)
1029 return nullptr;
1030 return &chips[idx];
1031}
1032
1033#else /* BOARD_MC33810_COUNT > 0 */
1034
1035int mc33810_add(brain_pin_e base, unsigned int index, const mc33810_config *cfg)
1036{
1037 (void)base; (void)index; (void)cfg;
1038
1039 return -5;
1040}
1041
1042const mc33810_state_s* mc33810getLiveData(size_t) {
1043 return nullptr;
1044}
1045
1046#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:795
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:745
static const char * mc33810_pin_names[MC33810_OUTPUTS]
Definition mc33810.cpp:205
int getMc33810maxDwellTimer(mc33810maxDwellTimer_e value)
Definition mc33810.cpp:1009
void mc33810_req_init()
Definition mc33810.cpp:999
const mc33810_state_s * mc33810getLiveData(size_t idx)
Definition mc33810.cpp:1027
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:949
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