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 /* Includes */
34 #include <stdint.h>
35 #include <stdbool.h>
36 #include <string.h>
37 
38 #include <ti/drivers/dpl/HwiP.h>
39 
40 #include <ti/drivers/Temperature.h>
41 #include <ti/drivers/temperature/TemperatureLPF3.h>
42 #include <ti/drivers/batterymonitor/BatMonSupportLPF3.h>
43 
44 #include <ti/drivers/utils/Math.h>
45 
46 #include <ti/devices/DeviceFamily.h>
47 #include DeviceFamily_constructPath(inc/hw_memmap.h)
48 #include DeviceFamily_constructPath(inc/hw_evtsvt.h)
49 #include DeviceFamily_constructPath(inc/hw_evtull.h)
50 #include DeviceFamily_constructPath(inc/hw_ints.h)
51 #include DeviceFamily_constructPath(inc/hw_pmud.h)
52 #include DeviceFamily_constructPath(inc/hw_pmctl.h)
53 #include DeviceFamily_constructPath(inc/hw_types.h)
54 #include DeviceFamily_constructPath(driverlib/interrupt.h)
55 
56 /* Macros */
57 /* Offset to apply to all thresholds before programming the hardware.
58  *
59  * When the hardware samples the temperature sensor, the returned reading
60  * is drawn from a probability distribution of measurements for each true
61  * temperature. In order for a notification to trigger, two separate
62  * measurements must cross the configured threshold temperature.
63  * The first is initiated by the hardware in the background to check whether
64  * to trigger the HWI. The second is triggered by software within the HWI
65  * function. If the first measurement is part of the long tail of the
66  * distribution, it is highly probable that the second measurement will not
67  * cross the threshold. This is effectively a spurrious interrupt that wastes
68  * energy and CPU cycles.
69  * If we program the actual hardware registers with an additional offset, we
70  * effectively shift the distribution up or down such that the first measurement
71  * triggers on the long tail of the distribution that is DISTRIBUTION_OFFSET
72  * degrees away from the threshold provided by the application. The second
73  * measurement then has a much higher probability of crossing the threshold
74  * and triggering the notification and an update of the thresholds.
75  *
76  * The risk is of course that a particular chip has a compressed distribution
77  * where the long tail does not reach far enough to trigger the HWI with the
78  * offset applied.
79  * Additionally, the device does not sample nearly as frequently when in
80  * standby.
81  *
82  * Both of these risks result in a less accurate but overall more useful system.
83  * If the temperature keeps rising, both scenarios still cause a notification
84  * to trigger. Given that temperature changes are usually not instantaneous,
85  * this should be considered an acceptable risk.
86  */
87 #define DISTRIBUTION_OFFSET 2
88 
89 #define BATMON_TEMPERATURE_MAX (255)
90 #define BATMON_TEMPERATURE_MIN (-256)
91 
92 #define INVALID_TEMPERATURE_MAX BATMON_TEMPERATURE_MAX
93 #define INVALID_TEMPERATURE_MIN BATMON_TEMPERATURE_MIN
94 
95 /* Forward declarations */
96 static void walkNotifyList(void);
97 static void setNextThresholds(void);
98 static void temperatureEventCb(uint32_t eventMask);
99 static void updateThresholds(int16_t thresholdHigh, int16_t thresholdLow);
100 static uint32_t degreesToCode(int32_t temperatureDegreesC);
101 static void setTempLowerLimit(int16_t thresholdLow);
102 static void setTempUpperLimit(int16_t thresholdHigh);
103 static void enableTempLowerLimit(void);
104 static void enableTempUpperLimit(void);
105 static void disableTempLowerLimit(void);
106 static void disableTempUpperLimit(void);
107 static void clearEventFlags(void);
108 static void thermalShutdownCallback(int16_t currentTemperature,
109                                     int16_t thresholdTemperature,
110                                     uintptr_t clientArg,
111                                     Temperature_NotifyObj *notifyObject);
112 static int16_t TemperatureLPF3_scaleToRealTemperature(int32_t temperature);
113 static int16_t TemperatureLPF3_scaleFromRealTemperature(int32_t temperature);
114 
115 /* Globals */
116 
117 /* Global list that stores all registered notifications */
118 static List_List notificationList;
119 
120 /* Current threshold values. These should always reflect the state of the
121  * batmon registers without the need to read them out, shift down, and sign
122  * extend the values.
123  */
124 static volatile int16_t currentThresholdHigh = INVALID_TEMPERATURE_MAX;
125 static volatile int16_t currentThresholdLow  = INVALID_TEMPERATURE_MIN;
126 
127 static bool isInitialized = false;
128 
129 /*
130  *  This object is used to monitor the temperature and trigger thermal shutdown once the threshold is reached.
131  */
132 static Temperature_NotifyObj thermalShutdown;
133 
134 /*
135  *  ======== degreesToCode ========
136  */
degreesToCode(int32_t temperatureDegreesC)137 static uint32_t degreesToCode(int32_t temperatureDegreesC)
138 {
139     /* Adjust for BATMON temperature offset */
140     temperatureDegreesC = TemperatureLPF3_scaleFromRealTemperature(temperatureDegreesC);
141 
142     uint32_t temperatureCode = (temperatureDegreesC << PMUD_TEMP_INT_S) & PMUD_TEMP_INT_M;
143 
144     return temperatureCode;
145 }
146 
147 /*
148  *  ======== setTempLowerLimit ========
149  */
setTempLowerLimit(int16_t thresholdLow)150 static void setTempLowerLimit(int16_t thresholdLow)
151 {
152     uint32_t temperatureCode = degreesToCode(thresholdLow - DISTRIBUTION_OFFSET);
153 
154     HWREG(PMUD_BASE + PMUD_O_TEMPLL) = temperatureCode;
155 
156     currentThresholdLow = thresholdLow;
157 }
158 
159 /*
160  *  ======== setTempUpperLimit ========
161  */
setTempUpperLimit(int16_t thresholdHigh)162 static void setTempUpperLimit(int16_t thresholdHigh)
163 {
164     uint32_t temperatureCode = degreesToCode(thresholdHigh + DISTRIBUTION_OFFSET);
165 
166     HWREG(PMUD_BASE + PMUD_O_TEMPUL) = temperatureCode;
167 
168     currentThresholdHigh = thresholdHigh;
169 }
170 
171 /*
172  *  ======== enableTempLowerLimit ========
173  */
enableTempLowerLimit(void)174 static void enableTempLowerLimit(void)
175 {
176     HWREG(PMUD_BASE + PMUD_O_EVENTMASK) |= PMUD_EVENTMASK_TEMP_BELOW_LL_MASK;
177 }
178 
179 /*
180  *  ======== enableTempUpperLimit ========
181  */
enableTempUpperLimit(void)182 static void enableTempUpperLimit(void)
183 {
184     HWREG(PMUD_BASE + PMUD_O_EVENTMASK) |= PMUD_EVENTMASK_TEMP_OVER_UL_MASK;
185 }
186 
187 /*
188  *  ======== disableTempLowerLimit ========
189  */
disableTempLowerLimit(void)190 static void disableTempLowerLimit(void)
191 {
192     HWREG(PMUD_BASE + PMUD_O_EVENTMASK) &= ~PMUD_EVENTMASK_TEMP_BELOW_LL_MASK;
193 }
194 
195 /*
196  *  ======== disableTempUpperLimit ========
197  */
disableTempUpperLimit(void)198 static void disableTempUpperLimit(void)
199 {
200     HWREG(PMUD_BASE + PMUD_O_EVENTMASK) &= ~PMUD_EVENTMASK_TEMP_OVER_UL_MASK;
201 }
202 
203 /*
204  *  ======== clearEventFlags ========
205  */
clearEventFlags(void)206 static void clearEventFlags(void)
207 {
208     HWREG(PMUD_BASE + PMUD_O_EVENT) &= PMUD_EVENT_TEMP_BELOW_LL | PMUD_EVENT_TEMP_OVER_UL;
209 }
210 
211 /*
212  *  ======== setNextThresholds ========
213  */
setNextThresholds(void)214 static void setNextThresholds(void)
215 {
216     List_Elem *notifyLink;
217     int16_t nextThresholdHigh = INVALID_TEMPERATURE_MAX;
218     int16_t nextThresholdLow  = INVALID_TEMPERATURE_MIN;
219     uint32_t key;
220 
221     key = HwiP_disable();
222 
223     /* Starting with the head of the list, keep track of the smallest high
224      * threshold and largest low threshold.
225      */
226     notifyLink = List_head(&notificationList);
227 
228     while (notifyLink != NULL)
229     {
230         Temperature_NotifyObj *notifyObject = (Temperature_NotifyObj *)notifyLink;
231 
232         nextThresholdHigh = Math_MIN(nextThresholdHigh, notifyObject->thresholdHigh);
233         nextThresholdLow  = Math_MAX(nextThresholdLow, notifyObject->thresholdLow);
234 
235         notifyLink = List_next(notifyLink);
236     }
237 
238     /* Now that we have found the next upper and lower thresholds, set them.
239      * These could be INVALID_TEMPERATURE_MAX and/or INVALID_TEMPERATURE_MIN
240      * if the list is empty or only high/low notifications were registered.
241      */
242     updateThresholds(nextThresholdHigh, nextThresholdLow);
243 
244     HwiP_restore(key);
245 }
246 
247 /*
248  *  ======== walkNotifyList ========
249  */
walkNotifyList(void)250 static void walkNotifyList(void)
251 {
252     List_Elem *notifyLink      = List_head(&notificationList);
253     int16_t currentTemperature = Temperature_getTemperature();
254 
255     /* If the notification list is empty, the head pointer will be
256      * NULL and the while loop will never execute the statement.
257      */
258     while (notifyLink != NULL)
259     {
260         Temperature_NotifyObj *notifyObject = (Temperature_NotifyObj *)notifyLink;
261 
262         /* Buffer the next link in case the notification triggers.
263          * Without buffering, we might skip list entries if the
264          * notifyObject is freed or reregistered and the notifyObject->link.next
265          * pointer is altered.
266          */
267         List_Elem *notifyLinkNext = List_next(notifyLink);
268 
269         /* If the current temperature is below this notification's low
270          * threshold or above its high threshold, remove it from the list and
271          * call the callback fxn
272          */
273         if (currentTemperature <= notifyObject->thresholdLow || currentTemperature >= notifyObject->thresholdHigh)
274         {
275 
276             /* Choose the threshold to provide to the notifyFxn based on the
277              * thresholds and the current temperature.
278              */
279             int16_t threshold = (currentTemperature <= notifyObject->thresholdLow) ? notifyObject->thresholdLow
280                                                                                    : notifyObject->thresholdHigh;
281 
282             List_remove(&notificationList, notifyLink);
283             notifyObject->isRegistered = false;
284 
285             notifyObject->notifyFxn(currentTemperature, threshold, notifyObject->clientArg, notifyObject);
286         }
287 
288         notifyLink = notifyLinkNext;
289     }
290 }
291 
292 /*
293  *  ======== updateThresholds ========
294  */
updateThresholds(int16_t thresholdHigh,int16_t thresholdLow)295 static void updateThresholds(int16_t thresholdHigh, int16_t thresholdLow)
296 {
297     if (thresholdHigh < currentThresholdHigh)
298     {
299         setTempUpperLimit(thresholdHigh);
300         enableTempUpperLimit();
301     }
302 
303     if (thresholdLow > currentThresholdLow)
304     {
305         setTempLowerLimit(thresholdLow);
306         enableTempLowerLimit();
307     }
308 }
309 
310 /*
311  *  ======== temperatureEventCb ========
312  *
313  *  Batmon interrupt triggered on high or low temperature event
314  */
temperatureEventCb(uint32_t eventMask)315 static void temperatureEventCb(uint32_t eventMask)
316 {
317 
318     setTempUpperLimit(INVALID_TEMPERATURE_MAX);
319     disableTempUpperLimit();
320 
321     setTempLowerLimit(INVALID_TEMPERATURE_MIN);
322     disableTempLowerLimit();
323 
324     /* Walk the notification list and issue any callbacks that have triggered
325      * at the current temperature.
326      */
327     walkNotifyList();
328 
329     /* Walk the queue another time to find and set the next set of thresholds.
330      * This is faster than making even one extra access to AON_BATMON.
331      */
332     setNextThresholds();
333 
334     /* Clear event flags. They may not immediately clear properly. */
335     clearEventFlags();
336 }
337 
338 /*
339  *  ======== thermalShutdownCallback ========
340  *
341  *  Callback function for thermal shutdown notify object
342  */
thermalShutdownCallback(int16_t currentTemperature,int16_t thresholdTemperature,uintptr_t clientArg,Temperature_NotifyObj * notifyObject)343 static void thermalShutdownCallback(int16_t currentTemperature,
344                                     int16_t thresholdTemperature,
345                                     uintptr_t clientArg,
346                                     Temperature_NotifyObj *notifyObject)
347 {
348     /* Immediately bring the device into reset, and enable the thermal shutdown comparator. */
349     TemperatureLPF3_triggerThermalShutdown();
350 }
351 
352 /*
353  *  ======== Temperature_init ========
354  */
Temperature_init(void)355 void Temperature_init(void)
356 {
357     uint32_t key;
358 
359     key = HwiP_disable();
360 
361     if (isInitialized == false)
362     {
363         BatMonSupportLPF3_init();
364         BatMonSupportLPF3_registerTemperatureCb(PMUD_EVENT_TEMP_BELOW_LL | PMUD_EVENT_TEMP_OVER_UL, temperatureEventCb);
365 
366         /* Wait first measurement is ready to prevent Temperature_getTemperature
367          * returning an invalid value
368          */
369         while ((HWREG(PMUD_BASE + PMUD_O_TEMPUPD) & PMUD_TEMPUPD_STA_M) != PMUD_TEMPUPD_STA_M) {}
370 
371         isInitialized = true;
372     }
373 
374     HwiP_restore(key);
375 }
376 
377 /*
378  *  ======== Temperature_getTemperature ========
379  */
Temperature_getTemperature(void)380 int16_t Temperature_getTemperature(void)
381 {
382     /* The temperature on Low Power F3 devices is stored in a 32-bit register
383      * containing a 9-bit signed integer part and a 2-bit unsigned fractional
384      * part. The fractional part is discarded from the returned result, but is
385      * used in the correction-calculation below.
386      */
387 
388     int32_t temperature = HWREG(PMUD_BASE + PMUD_O_TEMP);
389 
390     /* Mask and shift the integer and fractional parts of the temperature */
391     temperature = (temperature & (PMUD_TEMP_INT_M | PMUD_TEMP_FRAC_M)) >> PMUD_TEMP_FRAC_S;
392 
393     /* Perform sign extension */
394     temperature = (temperature << (32 - (PMUD_TEMP_INT_W + PMUD_TEMP_FRAC_W))) >>
395                   (32 - (PMUD_TEMP_INT_W + PMUD_TEMP_FRAC_W));
396 
397     /* Correct for temperature-dependent error in BATMON sensor */
398     temperature = TemperatureLPF3_scaleToRealTemperature(temperature);
399 
400     return temperature;
401 }
402 
403 /*
404  *  ======== Temperature_registerNotifyHigh ========
405  */
Temperature_registerNotifyHigh(Temperature_NotifyObj * notifyObject,int16_t thresholdHigh,Temperature_NotifyFxn notifyFxn,uintptr_t clientArg)406 int_fast16_t Temperature_registerNotifyHigh(Temperature_NotifyObj *notifyObject,
407                                             int16_t thresholdHigh,
408                                             Temperature_NotifyFxn notifyFxn,
409                                             uintptr_t clientArg)
410 {
411     uint32_t key;
412 
413     key = HwiP_disable();
414 
415     notifyObject->thresholdHigh = thresholdHigh;
416     notifyObject->thresholdLow  = INVALID_TEMPERATURE_MIN;
417     notifyObject->notifyFxn     = notifyFxn;
418     notifyObject->clientArg     = clientArg;
419 
420     if (notifyObject->isRegistered == false)
421     {
422         /* Add the notification to the end of the list.
423          * There is the implicit assumption that the notification is not already
424          * in the list. Otherwise the list linkage will be corrupted.
425          */
426         List_put(&notificationList, &notifyObject->link);
427 
428         notifyObject->isRegistered = true;
429     }
430 
431     updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
432 
433     HwiP_restore(key);
434 
435     return Temperature_STATUS_SUCCESS;
436 }
437 
438 /*
439  *  ======== Temperature_registerNotifyLow ========
440  */
Temperature_registerNotifyLow(Temperature_NotifyObj * notifyObject,int16_t thresholdLow,Temperature_NotifyFxn notifyFxn,uintptr_t clientArg)441 int_fast16_t Temperature_registerNotifyLow(Temperature_NotifyObj *notifyObject,
442                                            int16_t thresholdLow,
443                                            Temperature_NotifyFxn notifyFxn,
444                                            uintptr_t clientArg)
445 {
446     uint32_t key;
447 
448     key = HwiP_disable();
449 
450     notifyObject->thresholdHigh = INVALID_TEMPERATURE_MAX;
451     notifyObject->thresholdLow  = thresholdLow;
452     notifyObject->notifyFxn     = notifyFxn;
453     notifyObject->clientArg     = clientArg;
454 
455     if (notifyObject->isRegistered == false)
456     {
457         /* Add the notification to the end of the list.
458          * There is the implicit assumption that the notification is not already
459          * in the list. Otherwise the list linkage will be corrupted.
460          */
461         List_put(&notificationList, &notifyObject->link);
462 
463         notifyObject->isRegistered = true;
464     }
465 
466     updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
467 
468     HwiP_restore(key);
469 
470     return Temperature_STATUS_SUCCESS;
471 }
472 
473 /*
474  *  ======== Temperature_registerNotifyRange ========
475  */
Temperature_registerNotifyRange(Temperature_NotifyObj * notifyObject,int16_t thresholdHigh,int16_t thresholdLow,Temperature_NotifyFxn notifyFxn,uintptr_t clientArg)476 int_fast16_t Temperature_registerNotifyRange(Temperature_NotifyObj *notifyObject,
477                                              int16_t thresholdHigh,
478                                              int16_t thresholdLow,
479                                              Temperature_NotifyFxn notifyFxn,
480                                              uintptr_t clientArg)
481 {
482     uint32_t key;
483 
484     key = HwiP_disable();
485 
486     notifyObject->thresholdHigh = thresholdHigh;
487     notifyObject->thresholdLow  = thresholdLow;
488     notifyObject->notifyFxn     = notifyFxn;
489     notifyObject->clientArg     = clientArg;
490 
491     if (notifyObject->isRegistered == false)
492     {
493         /* Add the notification to the end of the list.
494          * There is the implicit assumption that the notification is not already
495          * in the list. Otherwise the list linkage will be corrupted.
496          */
497         List_put(&notificationList, &notifyObject->link);
498 
499         notifyObject->isRegistered = true;
500     }
501 
502     updateThresholds(notifyObject->thresholdHigh, notifyObject->thresholdLow);
503 
504     HwiP_restore(key);
505 
506     return Temperature_STATUS_SUCCESS;
507 }
508 
509 /*
510  *  ======== Temperature_unregisterNotify ========
511  */
Temperature_unregisterNotify(Temperature_NotifyObj * notifyObject)512 int_fast16_t Temperature_unregisterNotify(Temperature_NotifyObj *notifyObject)
513 {
514     uint32_t key;
515 
516     key = HwiP_disable();
517 
518     if (notifyObject->isRegistered == true)
519     {
520         /* Remove the notification from the list */
521         List_remove(&notificationList, &(notifyObject->link));
522 
523         notifyObject->isRegistered = false;
524     }
525 
526     /* Find the next set of thresholds and update the registers */
527     setNextThresholds();
528 
529     HwiP_restore(key);
530 
531     return Temperature_STATUS_SUCCESS;
532 }
533 
534 /*
535  *  ======== TemperatureLPF3_enableTSDMonitoring ========
536  */
TemperatureLPF3_enableTSDMonitoring(int16_t shutdownThreshold)537 void TemperatureLPF3_enableTSDMonitoring(int16_t shutdownThreshold)
538 {
539     /*
540      * Register a notify object on threshold value, that calls the thermal shutdown callback.
541      * The callback will immediately trigger a thermal shutdown
542      */
543     Temperature_registerNotifyHigh(&thermalShutdown, shutdownThreshold, thermalShutdownCallback, (uintptr_t)NULL);
544 }
545 
546 /*
547  *  ======== TemperatureLPF3_disableTSDMonitoring ========
548  */
TemperatureLPF3_disableTSDMonitoring(void)549 void TemperatureLPF3_disableTSDMonitoring(void)
550 {
551     /*
552      * Unregister the thermal shutdown notify-object.
553      */
554     Temperature_unregisterNotify(&thermalShutdown);
555 }
556 
557 /*
558  *  ======== TemperatureLPF3_triggerThermalShutdown ========
559  */
TemperatureLPF3_triggerThermalShutdown(void)560 void TemperatureLPF3_triggerThermalShutdown(void)
561 {
562     HWREG(PMCTL_BASE + PMCTL_O_RSTCTL) |= PMCTL_RSTCTL_TSDEN_EN;
563 }
564 
565 /*
566  *  ======== TemperatureLPF3_scaleToRealTemperature ========
567  */
TemperatureLPF3_scaleToRealTemperature(int32_t temperature)568 static int16_t TemperatureLPF3_scaleToRealTemperature(int32_t temperature)
569 {
570     /* Due to a non-linearity in the BATMON temperature sensor, the temperature
571      * returned by the hardware should be adjusted to attain a more accurate
572      * temperature. The transfer function from batmon temperature to real
573      * temperature is as follows:
574      *
575      * T_real(T) = p1*T + p0
576      *
577      * The coefficients account for the input temperature containing two
578      * fractional bits, and the adjusted temperature will also contain two
579      * fractional bits.
580      *
581      * p1 = 1.04348435
582      * p0 = -6.71741627
583      *
584      * Scaled by 2^20 to fixed-point integers:
585      *
586      * p1 = 1094173
587      * p0 = -7043721
588      */
589 
590     int32_t p1 = 1094172;
591     int32_t p0 = -7043721;
592 
593     temperature = (temperature * p1) + p0;
594 
595     /* Round to nearest integer. Divide by 2^22 to bring scaled
596      * temperature with two fractional bits down to temperature with no
597      * fractional bits. Do division instead of right-shift, since temperature
598      * is a signed integer.
599      */
600     if (temperature > 0)
601     {
602         temperature = (temperature + (1 << 21)) / (1 << 22);
603     }
604     else
605     {
606         temperature = (temperature - (1 << 21)) / (1 << 22);
607     }
608 
609     return temperature;
610 }
611 
TemperatureLPF3_scaleFromRealTemperature(int32_t temperature)612 static int16_t TemperatureLPF3_scaleFromRealTemperature(int32_t temperature)
613 {
614     /* Due to a non-linearity in the BATMON temperature sensor, the temperature
615      * programmed in the hardware should be adjusted to reflect a more accruate
616      * temperature. The transfer function accurate temperature to BATMON
617      * temperature is as follows:
618      *
619      * T_batmon(T) = p1*T + p0
620      *
621      * p1 = 0.95832774109
622      * p0 = 6.43748636
623      *
624      * Scaled by 2^20 to fixed-point integers:
625      *
626      * p1 = 1004879
627      * p0 = 6750194
628      */
629     int32_t p1 = 1004879;
630     int32_t p0 = 6750194;
631 
632     /* Add two fractional bits */
633     temperature *= 4;
634 
635     /* Bring temperature from real temperature to BATMON temperature */
636     temperature = (temperature * p1) + p0;
637 
638     /* Round to nearest integer. Divide by 2^22 to bring scaled
639      * temperature with two fractional bits down to temperature with no
640      * fractional bits. Do division instead of right-shift, since temperature
641      * is a signed integer.
642      */
643     if (temperature > 0)
644     {
645         temperature = (temperature + (1 << 21)) / (1 << 22);
646     }
647     else
648     {
649         temperature = (temperature - (1 << 21)) / (1 << 22);
650     }
651 
652     /* Limit the temperature to valid BATMON temperatures */
653     if (temperature > BATMON_TEMPERATURE_MAX)
654     {
655         temperature = BATMON_TEMPERATURE_MAX;
656     }
657     else if (temperature < BATMON_TEMPERATURE_MIN)
658     {
659         temperature = BATMON_TEMPERATURE_MIN;
660     }
661 
662     return temperature;
663 }