rusEFI
The most advanced open source ECU
tle6240.cpp
Go to the documentation of this file.
1 /*
2  * tle6240.c
3  *
4  * TLE6240GP Smart 16-Channel Low-Side Switch
5  *
6  * All 16 channels can be controlled via the serial interface (SPI).
7  * In addition to the serial control it is possible to control channel 1 to 4
8  * and 9 to 12 direct in parallel with a separate input pin.
9  *
10  * Looks like 3.3v SI and SCLK are NOT possible (H above 0.7Vs required, that's 3.5v for 5.0Vs)
11  * 5 MHz SPI
12  * Update: looks like possible:
13  * DS page 3: "Compatible with 3 V Microcontrollers"
14  * DS page 12: "Input High Voltage 2.0 V min"
15  *
16  * @date Dec 29, 2018
17  * @author Andrey Belomutskiy, (c) 2012-2020
18  *
19  * @date Mar 06, 2019
20  * @author Andrey Gusakov, (c) 2019
21  */
22 
23 #include "pch.h"
24 
25 #include "gpio/gpio_ext.h"
26 #include "gpio/tle6240.h"
27 
28 #if defined(BOARD_TLE6240_COUNT) && (BOARD_TLE6240_COUNT > 0)
29 
30 /*
31  * TODO list:
32  * - add irq support with fallback to polling mode (now polling mode only)
33  * - handle low-active inputs (set with PRG pin). Now driver assume high-active
34  * - add way to export native pin data of direct driven outputs. To avoid
35  * call to tle6240_writePad that will finally call native gpio set/clear fn.
36  * In this case direct drive gpios should not be occupied by markUsed in init?
37  * - fill deinit function with some code?
38  * - support emergency shutdown using reset pin
39  * - convert diagnostic to some enum
40  * - use DMA (currently there is issue (?) with SPI+DMA on STM32F7xx)
41  */
42 
43 /*==========================================================================*/
44 /* Driver local definitions. */
45 /*==========================================================================*/
46 
47 #define DRIVER_NAME "tle6240"
48 
49 static bool drv_task_ready = false;
50 
51 typedef enum {
57 
58 /* set 0000b for channes == 0..7 and 1111b for channels 8..15 */
59 #define CMD_CHIP(ch) ((ch < 8) ? 0x00 : 0x0f)
60 /* Full Diagnoscit, data byte ignored */
61 #define CMD_FULL_DIAG(ch) (((0x00 | CMD_CHIP(ch)) << 8) | 0x00)
62 /* Get state of 8 paralled inputs and 1-bit Diagnostic, data byte ignored */
63 #define CMD_IO_SHORTDIAG(ch) (((0xc0 | CMD_CHIP(ch)) << 8) | 0x00)
64 /* Echo function test of SPI, SI will be connected to SO on next access */
65 #define CMD_ECHO(ch) (((0xA0 | CMD_CHIP(ch)) << 8) | 0x00)
66 /* in data ORed, Full diagnostic output on next access */
67 #define CMD_OR_DIAG(ch, data) (((0x30 | CMD_CHIP(ch)) << 8) | (data & 0xff))
68 /* in data ANDed, Full diagnostic output on next access */
69 #define CMD_AND_DIAG(ch, data) (((0xf0 | CMD_CHIP(ch)) << 8) | (data & 0xff))
70 
71 /*==========================================================================*/
72 /* Driver exported variables. */
73 /*==========================================================================*/
74 
75 /*==========================================================================*/
76 /* Driver local variables and types. */
77 /*==========================================================================*/
78 
79 /* OS */
80 SEMAPHORE_DECL(tle6240_wake, 10 /* or BOARD_TLE6240_COUNT ? */);
81 static THD_WORKING_AREA(tle6240_thread_1_wa, 256);
82 
83 /* Driver */
84 struct Tle6240 : public GpioChip {
85  int init() override;
86 
87  int writePad(size_t pin, int value) override;
88  brain_pin_diag_e getDiag(size_t pin) override;
89 
90 
91  // internal functions
92  int spi_rw(uint16_t tx, uint16_t *rx);
93  int update_output_and_diag();
94  int chip_init();
95 
96 
97  const tle6240_config *cfg;
98  /* cached output state - state last send to chip */
99  uint16_t o_state_cached;
100  /* state to be sended to chip */
101  uint16_t o_state;
102  /* direct driven output mask */
103  uint16_t o_direct_mask;
104  /* full diagnostic status */
105  uint16_t diag[2];
106  /* diagnostic for ch 8..15 was requsted by last access
107  * can skip one transaction next time */
108  bool diag_8_reguested;
109 
110  tle6240_drv_state drv_state;
111 };
112 
113 static Tle6240 chips[BOARD_TLE6240_COUNT];
114 
115 static const char* tle6240_pin_names[TLE6240_OUTPUTS] = {
116  "tle6240.OUT1", "tle6240.OUT2", "tle6240.OUT3", "tle6240.OUT4",
117  "tle6240.OUT5", "tle6240.OUT6", "tle6240.OUT7", "tle6240.OUT8",
118  "tle6240.OUT9", "tle6240.OUT10", "tle6240.OUT11", "tle6240.OUT12",
119  "tle6240.OUT13", "tle6240.OUT14", "tle6240.OUT15", "tle6240.OUT16",
120 };
121 
122 /*==========================================================================*/
123 /* Driver local functions. */
124 /*==========================================================================*/
125 
126 /**
127  * @brief TLE6240 send and receive routine.
128  * @details Sends and receives 16 bits. CS asserted before and released
129  * after transaction.
130  */
131 
132 int Tle6240::spi_rw(uint16_t tx, uint16_t *rx)
133 {
134  uint16_t rxb;
135  SPIDriver *spi = cfg->spi_bus;
136 
137  /* Acquire ownership of the bus. */
138  spiAcquireBus(spi);
139  /* Setup transfer parameters. */
140  spiStart(spi, &cfg->spi_config);
141  /* Slave Select assertion. */
142  spiSelect(spi);
143  /* Atomic transfer operations. */
144  rxb = spiPolledExchange(spi, tx);
145  /* Slave Select de-assertion. */
146  spiUnselect(spi);
147  /* Ownership release. */
148  spiReleaseBus(spi);
149 
150  if (rx)
151  *rx = rxb;
152 
153  /* no errors for now */
154  return 0;
155 }
156 
157 /**
158  * @brief TLE6240 send output registers data.
159  * @details Sends ORed data to register, also receive 2-bit diagnostic.
160  */
161 
162 int Tle6240::update_output_and_diag()
163 {
164  int ret;
165  uint16_t out_data;
166 
167  /* atomic */
168  /* set value only for non-direct driven pins */
169  out_data = o_state & (~o_direct_mask);
170  if (diag_8_reguested) {
171  /* diagnostic for OUT8..15 was requested on prev access */
172  ret = spi_rw(CMD_OR_DIAG(0, (out_data >> 0) & 0xff), &diag[1]);
173  ret |= spi_rw(CMD_OR_DIAG(8, (out_data >> 8) & 0xff), &diag[0]);
174  } else {
175  ret = spi_rw(CMD_OR_DIAG(0, (out_data >> 0) & 0xff), NULL);
176  ret |= spi_rw(CMD_OR_DIAG(8, (out_data >> 8) & 0xff), &diag[0]);
177  /* send same one more time to receive OUT8..15 diagnostic */
178  ret |= spi_rw(CMD_OR_DIAG(8, (out_data >> 8) & 0xff), &diag[1]);
179  }
180 
181  diag_8_reguested = false;
182  if (ret == 0) {
183  /* atomic */
184  o_state_cached = out_data;
185  diag_8_reguested = true;
186  }
187 
188  return ret;
189 }
190 
191 /**
192  * @brief TLE6240 chip init.
193  * @details Checks communication. Mark all used pins.
194  * Checks direct io signals integrity using test cmd.
195  * Reads initial diagnostic state.
196  */
197 
198 int Tle6240::chip_init()
199 {
200  int n;
201  int ret;
202  uint16_t rx;
203 
204  /* mark pins used */
205  //ret = gpio_pin_markUsed(cfg->spi_config.ssport, cfg->spi_config.sspad, DRIVER_NAME " CS");
206  ret = 0;
207  if (cfg->reset.port != NULL)
208  ret |= gpio_pin_markUsed(cfg->reset.port, cfg->reset.pad, DRIVER_NAME " RST");
209  for (n = 0; n < TLE6240_DIRECT_OUTPUTS; n++)
210  if (cfg->direct_io[n].port)
211  ret |= gpio_pin_markUsed(cfg->direct_io[n].port, cfg->direct_io[n].pad, DRIVER_NAME " DIRECT IO");
212 
213  if (ret) {
214  ret = -1;
215  goto err_gpios;
216  }
217 
218  /* release reset */
219  if (cfg->reset.port) {
220  palClearPort(cfg->reset.port,
221  PAL_PORT_BIT(cfg->reset.pad));
222  chThdSleepMilliseconds(1);
223  palSetPort(cfg->reset.port,
224  PAL_PORT_BIT(cfg->reset.pad));
225  chThdSleepMilliseconds(10);
226  }
227 
228  /* check SPI communication */
229  /* 0. set echo mode, chip number - don't care */
230  ret = spi_rw(CMD_ECHO(0), nullptr);
231  /* 1. check loopback */
232  ret |= spi_rw(0x5555, &rx);
233  if (ret || (rx != 0x5555)) {
234  //print(DRIVER_NAME " spi loopback test failed\n");
235  ret = -2;
236  goto err_gpios;
237  }
238 
239  /* check direct io communication */
240  /* 0. set all direct out to 0 */
241  for (n = 0; n < TLE6240_DIRECT_OUTPUTS; n++) {
242  int i = (n < 4) ? n : (n + 4);
243  if (o_direct_mask & (1 << i)) {
244  palClearPort(cfg->direct_io[n].port,
245  PAL_PORT_BIT(cfg->direct_io[n].pad));
246  }
247  }
248  /* 1. disable IN0..7 outputs first (ADNed with 0x00)
249  * also will get full diag on next access */
250  ret = spi_rw(CMD_AND_DIAG(0, 0x00), NULL);
251  /* 2. get diag for OUT0..7 and send disable OUT8..15 */
252  ret |= spi_rw(CMD_AND_DIAG(8, 0x00), &diag[0]);
253  /* 3. get diag for OUT8..15 and readback input status */
254  ret |= spi_rw(CMD_IO_SHORTDIAG(0), &diag[1]);
255  /* 4. send dummy short diag command and get 8 bit of input data and
256  * 8 bit of short diag */
257  ret |= spi_rw(CMD_IO_SHORTDIAG(0), &rx);
258  rx = ((rx >> 4) & 0x0f00) | ((rx >> 8) & 0x000f);
259  if (ret || (rx & o_direct_mask)) {
260  //print(DRIVER_NAME " direct io test #1 failed (invalid io mask %04x)\n", (rx & chip->o_direct_mask));
261  ret = -3;
262  goto err_gpios;
263  }
264 
265  /* 5. set all direct io to 1 */
266  for (n = 0; n < TLE6240_DIRECT_OUTPUTS; n++) {
267  int i = (n < 4) ? n : (n + 4);
268  if (o_direct_mask & (1 << i)) {
269  palSetPort(cfg->direct_io[n].port,
270  PAL_PORT_BIT(cfg->direct_io[n].pad));
271  }
272  }
273  /* 6. read chort diagnostic again */
274  ret |= spi_rw(CMD_IO_SHORTDIAG(0), &rx);
275  rx = ((rx >> 4) & 0x0f00) | ((rx >> 8) & 0x000f);
276  rx &= o_direct_mask;
277  if (ret || (rx != o_direct_mask)) {
278  //print(DRIVER_NAME " direct io test #2 failed (invalid io mask %04x)\n", (rx ^ (~chip->o_direct_mask)));
279  ret = -4;
280  goto err_gpios;
281  }
282 
283  /* 7. set all all pins to OR mode, and upload pin states */
284  ret = update_output_and_diag();
285  if (ret) {
286  //print(DRIVER_NAME " final setup error\n");
287  ret = -5;
288  goto err_gpios;
289  }
290 
291  return 0;
292 
293 err_gpios:
294  /* unmark pins */
295  //gpio_pin_markUnused(cfg->spi_config.ssport, cfg->spi_config.sspad);
296  if (cfg->reset.port != NULL)
297  gpio_pin_markUnused(cfg->reset.port, cfg->reset.pad);
298  for (n = 0; n < TLE6240_DIRECT_OUTPUTS; n++)
299  if (cfg->direct_io[n].port)
300  gpio_pin_markUnused(cfg->direct_io[n].port, cfg->direct_io[n].pad);
301 
302  return ret;
303 }
304 
305 /**
306  * @brief TLE6240 chip driver wakeup.
307  * @details Wake up driver. Will cause output register and
308  * diagnostic update.
309  */
310 
312 {
313  /* Entering a reentrant critical zone.*/
314  chibios_rt::CriticalSectionLocker csl;
315 
316  chSemSignalI(&tle6240_wake);
317  if (!port_is_isr_context()) {
318  /**
319  * chSemSignalI above requires rescheduling
320  * interrupt handlers have implicit rescheduling
321  */
322  chSchRescheduleS();
323  }
324 
325  return 0;
326 }
327 
328 /*==========================================================================*/
329 /* Driver thread. */
330 /*==========================================================================*/
331 
332 static THD_FUNCTION(tle6240_driver_thread, p)
333 {
334  int i;
335  msg_t msg;
336 
337  (void)p;
338 
339  chRegSetThreadName(DRIVER_NAME);
340 
341  while(1) {
342  msg = chSemWaitTimeout(&tle6240_wake, TIME_MS2I(TLE6240_POLL_INTERVAL_MS));
343 
344  /* should we care about msg == MSG_TIMEOUT? */
345  (void)msg;
346 
347  for (i = 0; i < BOARD_TLE6240_COUNT; i++) {
348  int ret;
349  Tle6240& chip = chips[i];
350 
351  if (!chip.cfg ||
352  (chip.drv_state == TLE6240_DISABLED) ||
353  (chip.drv_state == TLE6240_FAILED))
354  continue;
355 
356  ret = chip.update_output_and_diag();
357  if (ret) {
358  /* set state to TLE6240_FAILED? */
359  }
360  }
361  }
362 }
363 
364 /*==========================================================================*/
365 /* Driver interrupt handlers. */
366 /*==========================================================================*/
367 
368 /* TODO: add IRQ support */
369 
370 /*==========================================================================*/
371 /* Driver exported functions. */
372 /*==========================================================================*/
373 
374 int Tle6240::writePad(unsigned int pin, int value)
375 {
376  if (pin >= TLE6240_OUTPUTS)
377  return -1;
378 
379  {
380  chibios_rt::CriticalSectionLocker csl;
381 
382  if (value)
383  o_state |= (1 << pin);
384  else
385  o_state &= ~(1 << pin);
386  }
387 
388  /* direct driven? */
389  if (o_direct_mask & (1 << pin)) {
390  int n = (pin < 8) ? pin : (pin - 4);
391 
392  /* TODO: ensure that TLE6240 configured in active high mode */
393  if (value)
394  palSetPort(cfg->direct_io[n].port,
395  PAL_PORT_BIT(cfg->direct_io[n].pad));
396  else
397  palClearPort(cfg->direct_io[n].port,
398  PAL_PORT_BIT(cfg->direct_io[n].pad));
399  } else {
401  }
402 
403  return 0;
404 }
405 
406 brain_pin_diag_e Tle6240::getDiag(size_t pin)
407 {
408  int val;
409  int diagVal;
410 
411  if (pin >= TLE6240_OUTPUTS)
412  return PIN_UNKNOWN;
413 
414  val = (diag[(pin > 7) ? 1 : 0] >> ((pin % 8) * 2)) & 0x03;
415  if (val == 0x3)
416  diagVal = PIN_OK;
417  else if (val == 0x2)
418  /* Overload, shorted load or overtemperature */
419  diagVal = PIN_OVERLOAD | PIN_DRIVER_OVERTEMP;
420  else if (val == 0x1)
421  diagVal = PIN_OPEN;
422  else if (val == 0x0)
423  diagVal = PIN_SHORT_TO_GND;
424 
425  return static_cast<brain_pin_diag_e>(diagVal);
426 }
427 
428 int Tle6240::init()
429 {
430  int ret = chip_init();
431  if (ret)
432  return ret;
433 
434  drv_state = TLE6240_READY;
435 
436  if (!drv_task_ready) {
437  chThdCreateStatic(tle6240_thread_1_wa, sizeof(tle6240_thread_1_wa),
438  PRIO_GPIOCHIP, tle6240_driver_thread, nullptr);
439  drv_task_ready = true;
440  }
441 
442  return 0;
443 }
444 
445 /**
446  * @brief TLE6240 driver add.
447  * @details Checks for valid config
448  */
449 
450 int tle6240_add(brain_pin_e base, unsigned int index, const tle6240_config *cfg)
451 {
452  int i;
453  int ret;
454  Tle6240 *chip;
455 
456  /* no config or no such chip */
457  if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_TLE6240_COUNT))
458  return -1;
459 
460  /* check for valid cs.
461  * TODO: remove this check? CS can be driven by SPI */
462  //if (cfg->spi_config.ssport == NULL)
463  // return -1;
464 
465  chip = &chips[index];
466 
467  /* already initted? */
468  if (chip->cfg != NULL)
469  return -1;
470 
471  chip->cfg = cfg;
472  chip->o_state = 0;
473  chip->o_state_cached = 0;
474  chip->o_direct_mask = 0;
475  chip->drv_state = TLE6240_WAIT_INIT;
476  for (i = 0; i < TLE6240_DIRECT_OUTPUTS; i++) {
477  if (cfg->direct_io[i].port != 0)
478  chip->o_direct_mask |= (1 << ((i < 4) ? i : (i + 4)));
479  }
480 
481  /* register, return gpio chip base */
482  ret = gpiochip_register(base, DRIVER_NAME, *chip, TLE6240_OUTPUTS);
483  if (ret < 0)
484  return ret;
485 
486  /* set default pin names, board init code can rewrite */
488 
489  return ret;
490 }
491 
492 #else /* BOARD_TLE6240_COUNT > 0 */
493 
494 int tle6240_add(brain_pin_e base, unsigned int index, const tle6240_config *cfg)
495 {
496  (void)base; (void)index; (void)cfg;
497 
498  return -1;
499 }
500 
501 #endif /* BOARD_TLE6240_COUNT */
Gpio
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition: core.cpp:125
int gpiochips_setPinNames(brain_pin_e base, const char **names)
Set pins names for registered gpiochip.
Definition: core.cpp:205
static SPIDriver * spi
Definition: hip9011.cpp:93
void writePad(const char *msg, brain_pin_e pin, int bit)
Definition: io_pins.cpp:115
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
Definition: rusefi_enums.h:43
virtual brain_pin_diag_e getDiag(size_t)
Definition: gpio_ext.h:30
virtual int writePad(size_t, int)
Definition: gpio_ext.h:28
virtual int init()=0
SPIDriver * spi_bus
Definition: tle6240.h:23
ioportid_t port
Definition: tle6240.h:27
struct tle6240_config::@37 direct_io[TLE6240_DIRECT_OUTPUTS]
int tle6240_add(brain_pin_e base, unsigned int index, const tle6240_config *cfg)
TLE6240 driver add.
Definition: tle6240.cpp:450
static bool drv_task_ready
Definition: tle6240.cpp:49
SEMAPHORE_DECL(tle6240_wake, 10)
static THD_WORKING_AREA(tle6240_thread_1_wa, 256)
static int tle6240_wake_driver()
TLE6240 chip driver wakeup.
Definition: tle6240.cpp:311
static Tle6240 chips[BOARD_TLE6240_COUNT]
Definition: tle6240.cpp:113
tle6240_drv_state
Definition: tle6240.cpp:51
@ TLE6240_WAIT_INIT
Definition: tle6240.cpp:53
@ TLE6240_DISABLED
Definition: tle6240.cpp:52
@ TLE6240_FAILED
Definition: tle6240.cpp:55
@ TLE6240_READY
Definition: tle6240.cpp:54
static const char * tle6240_pin_names[TLE6240_OUTPUTS]
Definition: tle6240.cpp:115
static THD_FUNCTION(tle6240_driver_thread, p)
Definition: tle6240.cpp:332