rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
tle9201.cpp
Go to the documentation of this file.
1/*
2 * tle9201.c
3 *
4 * TLE9201 H-Bridge with SPI
5 *
6 * @date Nov 1, 2024
7 *
8 * @author andreika, (c) 2024
9 * @author Andrey Belomutskiy, (c) 2012-2024
10 */
11
12#include "pch.h"
13
14#include "gpio/gpio_ext.h"
15#include "gpio/tle9201.h"
16
17#if EFI_PROD_CODE && (BOARD_TLE9201_COUNT > 0)
18
19/*==========================================================================*/
20/* Driver local definitions. */
21/*==========================================================================*/
22
23#define DRIVER_NAME "tle9201"
24
25#define TLE9201_REG_RD_DIA 0x00 // read diagnosis register
26#define TLE9201_REG_RES_DIA 0x80 // reset diagnosis register
27#define TLE9201_REG_RD_REV 0x20 // read device revision number
28#define TLE9201_REG_RD_CTRL 0x60 // read control register
29#define TLE9201_REG_WR_CTRL 0xE0 // write control register
30#define TLE9201_REG_WR_CTRL_RD_DIA 0xC0 // write control and read diagnosis
31
32#define TLE9201_REV_MAJOR_MASK 0xF0
33#define TLE9201_REV_MAJOR 0x20
34
35#define TLE9201_DIAG_CL (1<<4) // over current
36#define TLE9201_DIAG_TV (1<<5) // transmission validation
37#define TLE9201_DIAG_OT (1<<6) // over temperature
38#define TLE9201_DIAG_EN (1<<7) // outputs enabled
39#define TLE9201_DIAG_OUT_MASK 0xf // bits 0..3
40
41#define TLE9201_GET_VAL(rx) ((rx) & 0xff)
42
43static bool drv_task_ready = false;
44
50
51/*==========================================================================*/
52/* Driver exported variables. */
53/*==========================================================================*/
54
55/*==========================================================================*/
56/* Driver local variables and types. */
57/*==========================================================================*/
58
59static size_t diagOkCounter = 0;
60constexpr size_t DIAG_OK = 0xF;
61
62// Output states
63static const char *diagDiaOut[16] = { "?", "?", "?",
64 "VS Undervoltage", // 0x3
65 "?",
66 "Short to Bat at OUT1 and OUT2", // 0x5
67 "Short to GND at OUT1, short to Bat at OUT2", // 0x6
68 "Short to Bat at OUT2", // 0x7
69 "?",
70 "Short to Bat at OUT1, short to GND at OUT2", // 0x9
71 "Short to GND at OUT1 and OUT2", // 0xA
72 "Short to GND at OUT2", // 0xB
73 DRIVER_NAME " Open Load", // 0xC
74 "Short to Bat at OUT1", // 0xD
75 "Short to GND at OUT1", // 0xE
76 "No failure" // 0xF
77};
78
79/* OS */
80SEMAPHORE_DECL(tle9201_wake, 10 /* or BOARD_TLE9201_COUNT ? */);
81static THD_WORKING_AREA(tle9201_thread_1_wa, 256);
82
83/* Driver */
84struct Tle9201 {
85 int init(int i);
86
87 int spi_rw(uint16_t tx, uint16_t *rx);
88 int read_reg(uint8_t addr, uint8_t *val);
89
90 bool get_diag_and_rev(uint8_t *diag, uint8_t *rev);
91 void process_diag_and_rev(uint8_t diag, uint8_t rev);
92
93 const tle9201_config *cfg = NULL;
94
95 tle9201_drv_state drv_state;
96 int idx;
97 int detectedRev = 0;
98 uint8_t savedDiag = 0;
99 char name[10];
100};
101
102static Tle9201 chips[BOARD_TLE9201_COUNT];
103
104/*==========================================================================*/
105/* Driver local functions. */
106/*==========================================================================*/
107
108/**
109 * @brief TLE9201 send+receive routine.
110 */
111
112int Tle9201::spi_rw(uint16_t tx, uint16_t *rx) {
113 SPIDriver *spi = cfg->spi_bus;
114
115 /* Acquire ownership of the bus. */
116 spiAcquireBus(spi);
117 /* Setup transfer parameters. */
118 spiStart(spi, &cfg->spi_config);
119 /* Slave Select assertion. */
120 spiSelect(spi);
121 /* Atomic transfer operations. */
122 uint16_t rxd = spiPolledExchange(spi, tx);
123 /* Slave Select de-assertion. */
124 spiUnselect(spi);
125 /* Ownership release. */
126 spiReleaseBus(spi);
127
128 // return data
129 if (rx) {
130 *rx = rxd;
131 }
132
133 return 0;
134}
135
136int Tle9201::read_reg(uint8_t addr, uint8_t *val) {
137 int ret;
138
139 ret = spi_rw(addr, nullptr);
140 if (ret) {
141 return ret;
142 }
143
144 uint16_t rxd;
145 ret = spi_rw(addr, &rxd);
146 if (ret) {
147 return ret;
148 }
149
150 if (val) {
151 *val = TLE9201_GET_VAL(rxd);
152 }
153
154 return 0;
155}
156
157bool Tle9201::get_diag_and_rev(uint8_t *diag, uint8_t *rev) {
158 int retDiag = read_reg(TLE9201_REG_RD_DIA, diag);
159 int retRev = read_reg(TLE9201_REG_RD_REV, rev);
160 return (retDiag == 0 && retRev == 0);
161}
162
163void Tle9201::process_diag_and_rev(uint8_t diag, uint8_t rev) {
164 // react on revision change
165 if (rev != detectedRev) {
166 if ((rev & TLE9201_REV_MAJOR_MASK) == TLE9201_REV_MAJOR) {
167 efiPrintf("%s Detected! (rev=%08x)", name, rev);
168 } else {
169 efiPrintf("%s ERROR: Unknown revision (%08x)!", name, rev);
170 }
171
172 detectedRev = rev;
173 }
174
175 // react on diagnosis byte change
176 if (diag != savedDiag) {
177 efiPrintf("%s Diag (%08x):", name, diag);
178 // this bit should be always 0
179 if (diag & TLE9201_DIAG_TV) {
180 efiPrintf("* Diag status incorrect!");
181 }
182 // these bits are 0 if something happened
183 if (!(diag & TLE9201_DIAG_CL)) {
184 efiPrintf("* Overcurrent shutdown!");
185 }
186 if (!(diag & TLE9201_DIAG_OT)) {
187 efiPrintf("* Overtemperature shutdown!");
188 }
189 if (!(diag & TLE9201_DIAG_EN)) {
190 efiPrintf("* Outputs disabled.");
191 }
192 size_t statusCode = diag & TLE9201_DIAG_OUT_MASK;
193 if (statusCode == DIAG_OK) {
195 }
196 // print the status of the outputs
197 efiPrintf("* %s OK=%d", diagDiaOut[statusCode], diagOkCounter);
198
199 savedDiag = diag;
200 }
201}
202
203/*==========================================================================*/
204/* Driver thread. */
205/*==========================================================================*/
206
207static THD_FUNCTION(tle9201_driver_thread, p) {
208 bool wasSpiFailure = false;
209
210 (void)p;
211
212 chRegSetThreadName(DRIVER_NAME);
213
214 while (1) {
215 chThdSleepMilliseconds(TLE9201_POLL_INTERVAL_MS);
216
217static bool isInitialized = false;
218
219 if (!isIgnVoltage()) {
220 if (isInitialized) {
221 efiPrintf("Power loss? Would have to re-init TLE9201?");
222 isInitialized = false;
223 }
224 continue;
225 }
226
227
228 for (int i = 0; i < BOARD_TLE9201_COUNT; i++) {
229 auto chip = &chips[i];
230 if ((chip->cfg == NULL) ||
231 (chip->drv_state == TLE9201_DISABLED) ||
232 (chip->drv_state == TLE9201_FAILED)) {
233 continue;
234 }
235
236 uint8_t diag, rev;
237 // get diagnosis and revision bytes
238 if (!chip->get_diag_and_rev(&diag, &rev)) {
239 if (!wasSpiFailure) {
240 efiPrintf("%s ERROR: SPI failure!", chip->name);
241 wasSpiFailure = true;
242 }
243 } else {
244 wasSpiFailure = false;
245 }
246
247 chip->process_diag_and_rev(diag, rev);
248 }
249 }
250}
251
252/*==========================================================================*/
253/* Driver interrupt handlers. */
254/*==========================================================================*/
255
256/*==========================================================================*/
257/* Driver exported functions. */
258/*==========================================================================*/
259
260int Tle9201::init(int i) {
261 drv_state = TLE9201_READY;
262 idx = i;
263 sprintf(name, "TLE9201[%d]", idx);
264
265 if (!drv_task_ready) {
266 chThdCreateStatic(tle9201_thread_1_wa, sizeof(tle9201_thread_1_wa),
267 PRIO_GPIOCHIP, tle9201_driver_thread, NULL);
268 drv_task_ready = true;
269 }
270
271 return 0;
272}
273
274/**
275 * @brief TLE9201 driver add.
276 * @details Checks for valid config
277 */
278
279int tle9201_add(unsigned int index, const tle9201_config *cfg) {
280 /* no config or no such chip */
281 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_TLE9201_COUNT)) {
282 return -1;
283 }
284
285 if (index == 0) {
286 addConsoleAction("reset_9201", [](){
287 auto chip = &chips[0];
288 chip->spi_rw(TLE9201_REG_RES_DIA, nullptr);
289 efiPrintf(DRIVER_NAME "reset!");
290 });
291 }
292
293 /* check for valid cs.
294 * TODO: remove this check? CS can be driven by SPI */
295 //if (cfg->spi_config.ssport == NULL)
296 // return -1;
297
298 auto& chip = chips[index];
299
300 /* already initted? */
301 if (chip.cfg != NULL)
302 return -1;
303
304 chip.cfg = cfg;
305 chip.init(index);
306
307 return 0;
308}
309
310#else /* BOARD_TLE9201_COUNT > 0 */
311
312int tle9201_add(unsigned int index, const tle9201_config *cfg) {
313 (void)index; (void)cfg;
314
315 return -1;
316}
317
318#endif /* BOARD_TLE9201_COUNT */
constexpr uint8_t addr
Definition ads1015.cpp:14
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
bool isIgnVoltage()
SPIDriver * spi_bus
Definition tle9201.h:32
int tle9201_add(unsigned int index, const tle9201_config *cfg)
TLE9201 driver add.
Definition tle9201.cpp:279
static bool drv_task_ready
Definition tle9201.cpp:43
static THD_WORKING_AREA(tle9201_thread_1_wa, 256)
static Tle9201 chips[BOARD_TLE9201_COUNT]
Definition tle9201.cpp:102
static THD_FUNCTION(tle9201_driver_thread, p)
Definition tle9201.cpp:207
static const char * diagDiaOut[16]
Definition tle9201.cpp:63
tle9201_drv_state
Definition tle9201.cpp:45
@ TLE9201_DISABLED
Definition tle9201.cpp:46
@ TLE9201_FAILED
Definition tle9201.cpp:48
@ TLE9201_READY
Definition tle9201.cpp:47
constexpr size_t DIAG_OK
Definition tle9201.cpp:60
static size_t diagOkCounter
Definition tle9201.cpp:59
SEMAPHORE_DECL(tle9201_wake, 10)