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(&notificationList);
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(&notificationList);
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(&notificationList, 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(&notificationList, &notifyObject->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(&notificationList, &notifyObject->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(&notificationList, &notifyObject->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(&notificationList, &(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 }