1 /*
2  * Copyright (c) 2023 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <limits.h>
8 #include "ntc_thermistor.h"
9 
10 /**
11  * fixp_linear_interpolate() - interpolates a value from two known points
12  *
13  * @x0: x value of point 0
14  * @y0: y value of point 0
15  * @x1: x value of point 1
16  * @y1: y value of point 1
17  * @x: the linear interpolant
18  */
ntc_fixp_linear_interpolate(int x0,int y0,int x1,int y1,int x)19 static int ntc_fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x)
20 {
21 	if (y0 == y1 || x == x0) {
22 		return y0;
23 	}
24 	if (x1 == x0 || x == x1) {
25 		return y1;
26 	}
27 
28 	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
29 }
30 
31 /**
32  * Finds indices where ohm falls between.
33  *
34  * @param ohm key value search is looking for
35  * @param i_low return Lower interval index value
36  * @param i_high return Higher interval index value
37  */
ntc_lookup_comp(const struct ntc_type * type,unsigned int ohm,int * i_low,int * i_high)38 static void ntc_lookup_comp(const struct ntc_type *type, unsigned int ohm, int *i_low, int *i_high)
39 {
40 	int low = 0;
41 	int high = type->n_comp - 1;
42 
43 	if (ohm > type->comp[low].ohm) {
44 		high = low;
45 	} else if (ohm < type->comp[high].ohm) {
46 		low = high;
47 	}
48 
49 	while (high - low > 1) {
50 		int mid = (low + high) / 2;
51 
52 		if (ohm > type->comp[mid].ohm) {
53 			high = mid;
54 		} else {
55 			low = mid;
56 		}
57 	}
58 
59 	*i_low = low;
60 	*i_high = high;
61 }
62 
ntc_get_ohm_of_thermistor(const struct ntc_config * cfg,int sample_value,int sample_value_max)63 uint32_t ntc_get_ohm_of_thermistor(const struct ntc_config *cfg, int sample_value,
64 				   int sample_value_max)
65 {
66 	uint32_t ohm;
67 
68 	if (sample_value <= 0) {
69 		return cfg->connected_positive ? INT_MAX : 0;
70 	}
71 
72 	if (sample_value >= sample_value_max) {
73 		return cfg->connected_positive ? 0 : INT_MAX;
74 	}
75 
76 	if (cfg->connected_positive) {
77 		ohm = cfg->pulldown_ohm * (sample_value_max - sample_value) / sample_value;
78 	} else {
79 		ohm = cfg->pullup_ohm * sample_value / (sample_value_max - sample_value);
80 	}
81 
82 	return ohm;
83 }
84 
ntc_get_temp_mc(const struct ntc_type * type,unsigned int ohm)85 int32_t ntc_get_temp_mc(const struct ntc_type *type, unsigned int ohm)
86 {
87 	int low, high;
88 	int temp;
89 
90 	ntc_lookup_comp(type, ohm, &low, &high);
91 	/*
92 	 * First multiplying the table temperatures with 1000 to get to
93 	 * millicentigrades (which is what we want) and then interpolating
94 	 * will give the best precision.
95 	 */
96 	temp = ntc_fixp_linear_interpolate(type->comp[low].ohm, type->comp[low].temp_c * 1000,
97 					   type->comp[high].ohm, type->comp[high].temp_c * 1000,
98 					   ohm);
99 	return temp;
100 }
101