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(¬ificationList);
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(¬ificationList);
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(¬ificationList, 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(¬ificationList, ¬ifyObject->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(¬ificationList, ¬ifyObject->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(¬ificationList, ¬ifyObject->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(¬ificationList, &(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 }