1 /*
2 * Copyright (c) 2022-2023, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * ======== BatteryMonitorLPF3.c ========
35 *
36 */
37
38 /* Includes */
39 #include <stdint.h>
40 #include <stdbool.h>
41
42 #include <ti/drivers/dpl/HwiP.h>
43
44 #include <ti/drivers/BatteryMonitor.h>
45 #include <ti/drivers/batterymonitor/BatteryMonitorLPF3.h>
46 #include <ti/drivers/batterymonitor/BatMonSupportLPF3.h>
47
48 #include <ti/drivers/utils/Math.h>
49
50 #include <ti/devices/DeviceFamily.h>
51 #include DeviceFamily_constructPath(inc/hw_types.h)
52 #include DeviceFamily_constructPath(inc/hw_memmap.h)
53 #include DeviceFamily_constructPath(inc/hw_evtsvt.h)
54 #include DeviceFamily_constructPath(inc/hw_evtull.h)
55 #include DeviceFamily_constructPath(inc/hw_ints.h)
56 #include DeviceFamily_constructPath(inc/hw_pmud.h)
57 #include DeviceFamily_constructPath(inc/hw_pmctl.h)
58 #include DeviceFamily_constructPath(driverlib/interrupt.h)
59
60 /* Macros */
61 #define BATMON_VOLTAGE_MAX (7996)
62 #define BATMON_VOLTAGE_MIN (0)
63
64 #define INVALID_VOLTAGE_MAX BATMON_VOLTAGE_MAX
65 #define INVALID_VOLTAGE_MIN BATMON_VOLTAGE_MIN
66
67 /* Forward declarations */
68 static void walkNotifyList(uint16_t currentVoltage);
69 static void setNextThresholds(void);
70 static void batteryEventCb(uint32_t eventMask);
71 static void updateThresholds(uint16_t thresholdHigh, uint16_t thresholdLow);
72 static uint32_t millivoltsToCode(uint16_t voltageMillivolts);
73 static void setBatLowerLimit(uint16_t thresholdLow);
74 static void setBatUpperLimit(uint16_t thresholdHigh);
75 static void enableBatLowerLimit(void);
76 static void enableBatUpperLimit(void);
77 static void disableBatLowerLimit(void);
78 static void disableBatUpperLimit(void);
79 static void clearEventFlags(void);
80
81 /* Globals */
82
83 /* Global list that stores all registered notifications */
84 static List_List notificationList;
85
86 /* Current threshold values. These should always reflect the state of the
87 * BATMON registers without the need to read them out, and convert to
88 * millivolts.
89 */
90 static volatile uint16_t currentThresholdHigh = INVALID_VOLTAGE_MAX;
91 static volatile uint16_t currentThresholdLow = INVALID_VOLTAGE_MIN;
92
93 static bool isInitialized = false;
94
95 /*
96 * ======== millivoltsToCode ========
97 */
millivoltsToCode(uint16_t voltageMillivolts)98 static uint32_t millivoltsToCode(uint16_t voltageMillivolts)
99 {
100 /* Add fractional bits */
101 uint32_t voltageCode = voltageMillivolts << PMUD_BAT_INT_S;
102
103 /* Convert from millivolts to volts */
104 voltageCode = Math_divideBy1000(voltageCode);
105
106 /* Mask result */
107 voltageCode &= (PMUD_BAT_INT_M | PMUD_BAT_FRAC_M);
108
109 return voltageCode;
110 }
111
112 /*
113 * ======== setBatLowerLimit ========
114 */
setBatLowerLimit(uint16_t thresholdLow)115 static void setBatLowerLimit(uint16_t thresholdLow)
116 {
117 uint32_t voltageCode = millivoltsToCode(thresholdLow);
118
119 HWREG(PMUD_BASE + PMUD_O_BATTLL) = voltageCode;
120
121 currentThresholdLow = thresholdLow;
122 }
123
124 /*
125 * ======== setBatUpperLimit ========
126 */
setBatUpperLimit(uint16_t thresholdHigh)127 static void setBatUpperLimit(uint16_t thresholdHigh)
128 {
129 uint32_t voltageCode = millivoltsToCode(thresholdHigh);
130
131 HWREG(PMUD_BASE + PMUD_O_BATTUL) = voltageCode;
132
133 currentThresholdHigh = thresholdHigh;
134 }
135
136 /*
137 * ======== enableBatLowerLimit ========
138 */
enableBatLowerLimit(void)139 static void enableBatLowerLimit(void)
140 {
141 HWREG(PMUD_BASE + PMUD_O_EVENTMASK) |= PMUD_EVENTMASK_BATT_BELOW_LL_MASK;
142 }
143
144 /*
145 * ======== enableBatUpperLimit ========
146 */
enableBatUpperLimit(void)147 static void enableBatUpperLimit(void)
148 {
149 HWREG(PMUD_BASE + PMUD_O_EVENTMASK) |= PMUD_EVENTMASK_BATT_OVER_UL_MASK;
150 }
151
152 /*
153 * ======== disableBatLowerLimit ========
154 */
disableBatLowerLimit(void)155 static void disableBatLowerLimit(void)
156 {
157 HWREG(PMUD_BASE + PMUD_O_EVENTMASK) &= ~PMUD_EVENTMASK_BATT_BELOW_LL_MASK;
158 }
159
160 /*
161 * ======== disableBatUpperLimit ========
162 */
disableBatUpperLimit(void)163 static void disableBatUpperLimit(void)
164 {
165 HWREG(PMUD_BASE + PMUD_O_EVENTMASK) &= ~PMUD_EVENTMASK_BATT_OVER_UL_MASK;
166 }
167
168 /*
169 * ======== clearEventFlags ========
170 */
clearEventFlags(void)171 static void clearEventFlags(void)
172 {
173 HWREG(PMUD_BASE + PMUD_O_EVENT) &= (PMUD_EVENT_BATT_BELOW_LL | PMUD_EVENT_BATT_OVER_UL);
174 }
175
176 /*
177 * ======== setNextThresholds ========
178 */
setNextThresholds(void)179 static void setNextThresholds(void)
180 {
181 List_Elem *notifyLink;
182 int16_t nextThresholdHigh = INVALID_VOLTAGE_MAX;
183 int16_t nextThresholdLow = INVALID_VOLTAGE_MIN;
184 uint32_t key;
185
186 key = HwiP_disable();
187
188 /* Starting with the head of the list, keep track of the smallest high
189 * threshold and largest low threshold.
190 */
191 notifyLink = List_head(¬ificationList);
192
193 while (notifyLink != NULL)
194 {
195 BatteryMonitor_NotifyObj *notifyObject = (BatteryMonitor_NotifyObj *)notifyLink;
196
197 nextThresholdHigh = Math_MIN(nextThresholdHigh, notifyObject->thresholdHigh);
198 nextThresholdLow = Math_MAX(nextThresholdLow, notifyObject->thresholdLow);
199
200 notifyLink = List_next(notifyLink);
201 }
202
203 /* Now that we have found the next upper and lower thresholds, set them.
204 * These could be INVALID_VOLTAGE_MAX and/or INVALID_VOLTAGE_MIN
205 * if the list is empty or only high/low notifications were registered.
206 */
207 updateThresholds(nextThresholdHigh, nextThresholdLow);
208
209 HwiP_restore(key);
210 }
211
212 /*
213 * ======== walkNotifyList ========
214 */
walkNotifyList(uint16_t currentVoltage)215 static void walkNotifyList(uint16_t currentVoltage)
216 {
217 List_Elem *notifyLink = List_head(¬ificationList);
218
219 /* If the notification list is empty, the head pointer will be
220 * NULL and the while loop will never execute the statement.
221 */
222 while (notifyLink != NULL)
223 {
224 BatteryMonitor_NotifyObj *notifyObject = (BatteryMonitor_NotifyObj *)notifyLink;
225
226 /* Buffer the next link in case the notification triggers.
227 * Without buffering, we might skip list entries if the
228 * notifyObject is freed or re-registered and the notifyObject->link.next
229 * pointer is altered.
230 */
231 List_Elem *notifyLinkNext = List_next(notifyLink);
232
233 /* If the current voltage is below this notification's low
234 * threshold or above its high threshold, remove it from the list and
235 * call the callback fxn
236 */
237 if (currentVoltage <= notifyObject->thresholdLow || currentVoltage >= notifyObject->thresholdHigh)
238 {
239
240 /* Choose the threshold to provide to the notifyFxn based on the
241 * thresholds and the current voltage.
242 */
243 uint16_t threshold = (currentVoltage <= notifyObject->thresholdLow) ? notifyObject->thresholdLow
244 : notifyObject->thresholdHigh;
245
246 List_remove(¬ificationList, notifyLink);
247 notifyObject->isRegistered = false;
248
249 notifyObject->notifyFxn(currentVoltage, threshold, notifyObject->clientArg, notifyObject);
250 }
251
252 notifyLink = notifyLinkNext;
253 }
254 }
255
256 /*
257 * ======== updateThresholds ========
258 */
updateThresholds(uint16_t thresholdHigh,uint16_t thresholdLow)259 static void updateThresholds(uint16_t thresholdHigh, uint16_t thresholdLow)
260 {
261 if (thresholdHigh < currentThresholdHigh)
262 {
263 setBatUpperLimit(thresholdHigh);
264 enableBatUpperLimit();
265 }
266
267 if (thresholdLow > currentThresholdLow)
268 {
269 setBatLowerLimit(thresholdLow);
270 enableBatLowerLimit();
271 }
272 }
273
274 /*
275 * ======== batteryEventCb ========
276 *
277 * BATMON interrupt triggered on high or low battery event
278 */
batteryEventCb(uint32_t eventMask)279 static void batteryEventCb(uint32_t eventMask)
280 {
281 /* Get the current voltage */
282 uint16_t currentVoltage = BatteryMonitor_getVoltage();
283
284 /* Only walk through notify list if the current voltage is actually above the high threshold or below the low
285 * threshold. */
286 if (currentVoltage <= currentThresholdLow || currentVoltage >= currentThresholdHigh)
287 {
288 setBatUpperLimit(INVALID_VOLTAGE_MAX);
289 disableBatUpperLimit();
290
291 setBatLowerLimit(INVALID_VOLTAGE_MIN);
292 disableBatLowerLimit();
293
294 /* Walk the notification list and issue any callbacks that have triggered
295 * at the current voltage.
296 */
297 walkNotifyList(currentVoltage);
298
299 /* Walk the queue another time to find and set the next set of thresholds.
300 */
301 setNextThresholds();
302 }
303
304 /* Clear event flags. */
305 clearEventFlags();
306 }
307
308 /*
309 * ======== BatteryMonitor_init ========
310 */
BatteryMonitor_init(void)311 void BatteryMonitor_init(void)
312 {
313 uint32_t key;
314
315 key = HwiP_disable();
316
317 if (isInitialized == false)
318 {
319 BatMonSupportLPF3_init();
320 BatMonSupportLPF3_registerBatteryCb((PMUD_EVENT_BATT_BELOW_LL | PMUD_EVENT_BATT_OVER_UL), batteryEventCb);
321
322 /* Wait until first measurement is ready to prevent BatteryMonitor_getVoltage
323 * returning an invalid value.
324 */
325 while ((HWREG(PMUD_BASE + PMUD_O_BATUPD) & PMUD_BATUPD_STA_M) != PMUD_BATUPD_STA_M) {}
326
327 isInitialized = true;
328 }
329
330 HwiP_restore(key);
331 }
332
333 /*
334 * ======== BatteryMonitor_getVoltage ========
335 */
BatteryMonitor_getVoltage(void)336 uint16_t BatteryMonitor_getVoltage(void)
337 {
338 /* The voltage on LPF3 is stored in a 32-bit register
339 * containing a 3-bit unsigned integer part and a 8-bit unsigned fractional
340 * part.
341 */
342
343 /* Voltage in 3.8 fixed-point format (in Volts) */
344 uint32_t voltage = HWREG(PMUD_BASE + PMUD_O_BAT) & (PMUD_BAT_INT_M | PMUD_BAT_FRAC_M);
345
346 /* Convert to mV by multiplying by 1000 */
347 voltage *= 1000;
348
349 /* Round to nearest integer and discard fractional part */
350 voltage = (voltage + (1 << (PMUD_BAT_INT_S - 1))) >> PMUD_BAT_INT_S;
351
352 return voltage;
353 }
354
355 /*
356 * ======== BatteryMonitor_registerNotifyHigh ========
357 */
BatteryMonitor_registerNotifyHigh(BatteryMonitor_NotifyObj * notifyObject,uint16_t thresholdHigh,BatteryMonitor_NotifyFxn notifyFxn,uintptr_t clientArg)358 int_fast16_t BatteryMonitor_registerNotifyHigh(BatteryMonitor_NotifyObj *notifyObject,
359 uint16_t thresholdHigh,
360 BatteryMonitor_NotifyFxn notifyFxn,
361 uintptr_t clientArg)
362 {
363 uint32_t key;
364
365 key = HwiP_disable();
366
367 notifyObject->thresholdHigh = thresholdHigh;
368 notifyObject->thresholdLow = INVALID_VOLTAGE_MIN;
369 notifyObject->notifyFxn = notifyFxn;
370 notifyObject->clientArg = clientArg;
371
372 if (notifyObject->isRegistered == false)
373 {
374 /* Add the notification to the end of the list.
375 * There is the implicit assumption that the notification is not already
376 * in the list. Otherwise the list linkage will be corrupted.
377 */
378 List_put(¬ificationList, ¬ifyObject->link);
379
380 notifyObject->isRegistered = true;
381 }
382
383 updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
384
385 HwiP_restore(key);
386
387 return BatteryMonitor_STATUS_SUCCESS;
388 }
389
390 /*
391 * ======== BatteryMonitor_registerNotifyLow ========
392 */
BatteryMonitor_registerNotifyLow(BatteryMonitor_NotifyObj * notifyObject,uint16_t thresholdLow,BatteryMonitor_NotifyFxn notifyFxn,uintptr_t clientArg)393 int_fast16_t BatteryMonitor_registerNotifyLow(BatteryMonitor_NotifyObj *notifyObject,
394 uint16_t thresholdLow,
395 BatteryMonitor_NotifyFxn notifyFxn,
396 uintptr_t clientArg)
397 {
398 uint32_t key;
399
400 key = HwiP_disable();
401
402 notifyObject->thresholdHigh = INVALID_VOLTAGE_MAX;
403 notifyObject->thresholdLow = thresholdLow;
404 notifyObject->notifyFxn = notifyFxn;
405 notifyObject->clientArg = clientArg;
406
407 if (notifyObject->isRegistered == false)
408 {
409 /* Add the notification to the end of the list.
410 * There is the implicit assumption that the notification is not already
411 * in the list. Otherwise the list linkage will be corrupted.
412 */
413 List_put(¬ificationList, ¬ifyObject->link);
414
415 notifyObject->isRegistered = true;
416 }
417
418 updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
419
420 HwiP_restore(key);
421
422 return BatteryMonitor_STATUS_SUCCESS;
423 }
424
425 /*
426 * ======== BatteryMonitor_registerNotifyRange ========
427 */
BatteryMonitor_registerNotifyRange(BatteryMonitor_NotifyObj * notifyObject,uint16_t thresholdHigh,uint16_t thresholdLow,BatteryMonitor_NotifyFxn notifyFxn,uintptr_t clientArg)428 int_fast16_t BatteryMonitor_registerNotifyRange(BatteryMonitor_NotifyObj *notifyObject,
429 uint16_t thresholdHigh,
430 uint16_t thresholdLow,
431 BatteryMonitor_NotifyFxn notifyFxn,
432 uintptr_t clientArg)
433 {
434 uint32_t key;
435
436 key = HwiP_disable();
437
438 notifyObject->thresholdHigh = thresholdHigh;
439 notifyObject->thresholdLow = thresholdLow;
440 notifyObject->notifyFxn = notifyFxn;
441 notifyObject->clientArg = clientArg;
442
443 if (notifyObject->isRegistered == false)
444 {
445 /* Add the notification to the end of the list.
446 * There is the implicit assumption that the notification is not already
447 * in the list. Otherwise the list linkage will be corrupted.
448 */
449 List_put(¬ificationList, ¬ifyObject->link);
450
451 notifyObject->isRegistered = true;
452 }
453
454 updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
455
456 HwiP_restore(key);
457
458 return BatteryMonitor_STATUS_SUCCESS;
459 }
460
461 /*
462 * ======== BatteryMonitor_unregisterNotify ========
463 */
BatteryMonitor_unregisterNotify(BatteryMonitor_NotifyObj * notifyObject)464 int_fast16_t BatteryMonitor_unregisterNotify(BatteryMonitor_NotifyObj *notifyObject)
465 {
466 uint32_t key;
467
468 key = HwiP_disable();
469
470 if (notifyObject->isRegistered == true)
471 {
472 /* Remove the notification from the list */
473 List_remove(¬ificationList, &(notifyObject->link));
474
475 notifyObject->isRegistered = false;
476 }
477
478 /* Find the next set of thresholds and update the registers */
479 setNextThresholds();
480
481 HwiP_restore(key);
482
483 return BatteryMonitor_STATUS_SUCCESS;
484 }