xref: /openbmc/linux/drivers/hwmon/ntc_thermistor.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f22aaaa7SDonggeun Kim /*
3f22aaaa7SDonggeun Kim  * ntc_thermistor.c - NTC Thermistors
4f22aaaa7SDonggeun Kim  *
5f22aaaa7SDonggeun Kim  *  Copyright (C) 2010 Samsung Electronics
6f22aaaa7SDonggeun Kim  *  MyungJoo Ham <myungjoo.ham@samsung.com>
7f22aaaa7SDonggeun Kim  */
8f22aaaa7SDonggeun Kim 
9f22aaaa7SDonggeun Kim #include <linux/slab.h>
10f22aaaa7SDonggeun Kim #include <linux/module.h>
11f22aaaa7SDonggeun Kim #include <linux/math64.h>
1270760e80SLinus Walleij #include <linux/mod_devicetable.h>
13f22aaaa7SDonggeun Kim #include <linux/platform_device.h>
1470760e80SLinus Walleij #include <linux/property.h>
15f22aaaa7SDonggeun Kim #include <linux/err.h>
16bd56c1e9SLinus Walleij #include <linux/fixp-arith.h>
179e8269deSNaveen Krishna Chatradhi #include <linux/iio/consumer.h>
18f22aaaa7SDonggeun Kim #include <linux/hwmon.h>
19f22aaaa7SDonggeun Kim 
2011a24ca7SLinus Walleij enum ntc_thermistor_type {
2111a24ca7SLinus Walleij 	TYPE_B57330V2103,
2211a24ca7SLinus Walleij 	TYPE_B57891S0103,
2311a24ca7SLinus Walleij 	TYPE_NCPXXWB473,
2411a24ca7SLinus Walleij 	TYPE_NCPXXWF104,
2511a24ca7SLinus Walleij 	TYPE_NCPXXWL333,
2611a24ca7SLinus Walleij 	TYPE_NCPXXXH103,
2711a24ca7SLinus Walleij };
2811a24ca7SLinus Walleij 
29f22aaaa7SDonggeun Kim struct ntc_compensation {
30088ce2acSGuenter Roeck 	int		temp_c;
31f22aaaa7SDonggeun Kim 	unsigned int	ohm;
32f22aaaa7SDonggeun Kim };
33f22aaaa7SDonggeun Kim 
34e056fe25SPeter Rosin /*
35e056fe25SPeter Rosin  * Used as index in a zero-terminated array, holes not allowed so
36e056fe25SPeter Rosin  * that NTC_LAST is the first empty array entry.
37e056fe25SPeter Rosin  */
38e056fe25SPeter Rosin enum {
39e056fe25SPeter Rosin 	NTC_B57330V2103,
40e056fe25SPeter Rosin 	NTC_B57891S0103,
41e056fe25SPeter Rosin 	NTC_NCP03WB473,
42e056fe25SPeter Rosin 	NTC_NCP03WF104,
43e056fe25SPeter Rosin 	NTC_NCP15WB473,
44e056fe25SPeter Rosin 	NTC_NCP15WL333,
45e056fe25SPeter Rosin 	NTC_NCP15XH103,
46e056fe25SPeter Rosin 	NTC_NCP18WB473,
47e056fe25SPeter Rosin 	NTC_NCP21WB473,
48e13e979bSLinus Walleij 	NTC_SSG1404001221,
49e056fe25SPeter Rosin 	NTC_LAST,
50e056fe25SPeter Rosin };
51e056fe25SPeter Rosin 
529e8269deSNaveen Krishna Chatradhi static const struct platform_device_id ntc_thermistor_id[] = {
53e056fe25SPeter Rosin 	[NTC_B57330V2103]     = { "b57330v2103",     TYPE_B57330V2103 },
54e056fe25SPeter Rosin 	[NTC_B57891S0103]     = { "b57891s0103",     TYPE_B57891S0103 },
55e056fe25SPeter Rosin 	[NTC_NCP03WB473]      = { "ncp03wb473",      TYPE_NCPXXWB473 },
56e056fe25SPeter Rosin 	[NTC_NCP03WF104]      = { "ncp03wf104",      TYPE_NCPXXWF104 },
57e056fe25SPeter Rosin 	[NTC_NCP15WB473]      = { "ncp15wb473",      TYPE_NCPXXWB473 },
58e056fe25SPeter Rosin 	[NTC_NCP15WL333]      = { "ncp15wl333",      TYPE_NCPXXWL333 },
59e056fe25SPeter Rosin 	[NTC_NCP15XH103]      = { "ncp15xh103",      TYPE_NCPXXXH103 },
60e056fe25SPeter Rosin 	[NTC_NCP18WB473]      = { "ncp18wb473",      TYPE_NCPXXWB473 },
61e056fe25SPeter Rosin 	[NTC_NCP21WB473]      = { "ncp21wb473",      TYPE_NCPXXWB473 },
62e23e40fdSLinus Walleij 	[NTC_SSG1404001221]   = { "ssg1404_001221",  TYPE_NCPXXWB473 },
63e056fe25SPeter Rosin 	[NTC_LAST]            = { },
649e8269deSNaveen Krishna Chatradhi };
65cbb2313eSYuntao Liu MODULE_DEVICE_TABLE(platform, ntc_thermistor_id);
669e8269deSNaveen Krishna Chatradhi 
67f22aaaa7SDonggeun Kim /*
68f22aaaa7SDonggeun Kim  * A compensation table should be sorted by the values of .ohm
69f22aaaa7SDonggeun Kim  * in descending order.
70f22aaaa7SDonggeun Kim  * The following compensation tables are from the specification of Murata NTC
71f22aaaa7SDonggeun Kim  * Thermistors Datasheet
72f22aaaa7SDonggeun Kim  */
734626dcffSSachin Kamat static const struct ntc_compensation ncpXXwb473[] = {
74088ce2acSGuenter Roeck 	{ .temp_c	= -40, .ohm	= 1747920 },
75088ce2acSGuenter Roeck 	{ .temp_c	= -35, .ohm	= 1245428 },
76088ce2acSGuenter Roeck 	{ .temp_c	= -30, .ohm	= 898485 },
77088ce2acSGuenter Roeck 	{ .temp_c	= -25, .ohm	= 655802 },
78088ce2acSGuenter Roeck 	{ .temp_c	= -20, .ohm	= 483954 },
79088ce2acSGuenter Roeck 	{ .temp_c	= -15, .ohm	= 360850 },
80088ce2acSGuenter Roeck 	{ .temp_c	= -10, .ohm	= 271697 },
81088ce2acSGuenter Roeck 	{ .temp_c	= -5, .ohm	= 206463 },
82088ce2acSGuenter Roeck 	{ .temp_c	= 0, .ohm	= 158214 },
83088ce2acSGuenter Roeck 	{ .temp_c	= 5, .ohm	= 122259 },
84088ce2acSGuenter Roeck 	{ .temp_c	= 10, .ohm	= 95227 },
85088ce2acSGuenter Roeck 	{ .temp_c	= 15, .ohm	= 74730 },
86088ce2acSGuenter Roeck 	{ .temp_c	= 20, .ohm	= 59065 },
87088ce2acSGuenter Roeck 	{ .temp_c	= 25, .ohm	= 47000 },
88088ce2acSGuenter Roeck 	{ .temp_c	= 30, .ohm	= 37643 },
89088ce2acSGuenter Roeck 	{ .temp_c	= 35, .ohm	= 30334 },
90088ce2acSGuenter Roeck 	{ .temp_c	= 40, .ohm	= 24591 },
91088ce2acSGuenter Roeck 	{ .temp_c	= 45, .ohm	= 20048 },
92088ce2acSGuenter Roeck 	{ .temp_c	= 50, .ohm	= 16433 },
93088ce2acSGuenter Roeck 	{ .temp_c	= 55, .ohm	= 13539 },
94088ce2acSGuenter Roeck 	{ .temp_c	= 60, .ohm	= 11209 },
95088ce2acSGuenter Roeck 	{ .temp_c	= 65, .ohm	= 9328 },
96088ce2acSGuenter Roeck 	{ .temp_c	= 70, .ohm	= 7798 },
97088ce2acSGuenter Roeck 	{ .temp_c	= 75, .ohm	= 6544 },
98088ce2acSGuenter Roeck 	{ .temp_c	= 80, .ohm	= 5518 },
99088ce2acSGuenter Roeck 	{ .temp_c	= 85, .ohm	= 4674 },
100088ce2acSGuenter Roeck 	{ .temp_c	= 90, .ohm	= 3972 },
101088ce2acSGuenter Roeck 	{ .temp_c	= 95, .ohm	= 3388 },
102088ce2acSGuenter Roeck 	{ .temp_c	= 100, .ohm	= 2902 },
103088ce2acSGuenter Roeck 	{ .temp_c	= 105, .ohm	= 2494 },
104088ce2acSGuenter Roeck 	{ .temp_c	= 110, .ohm	= 2150 },
105088ce2acSGuenter Roeck 	{ .temp_c	= 115, .ohm	= 1860 },
106088ce2acSGuenter Roeck 	{ .temp_c	= 120, .ohm	= 1615 },
107088ce2acSGuenter Roeck 	{ .temp_c	= 125, .ohm	= 1406 },
108f22aaaa7SDonggeun Kim };
1094626dcffSSachin Kamat static const struct ntc_compensation ncpXXwl333[] = {
110088ce2acSGuenter Roeck 	{ .temp_c	= -40, .ohm	= 1610154 },
111088ce2acSGuenter Roeck 	{ .temp_c	= -35, .ohm	= 1130850 },
112088ce2acSGuenter Roeck 	{ .temp_c	= -30, .ohm	= 802609 },
113088ce2acSGuenter Roeck 	{ .temp_c	= -25, .ohm	= 575385 },
114088ce2acSGuenter Roeck 	{ .temp_c	= -20, .ohm	= 416464 },
115088ce2acSGuenter Roeck 	{ .temp_c	= -15, .ohm	= 304219 },
116088ce2acSGuenter Roeck 	{ .temp_c	= -10, .ohm	= 224193 },
117088ce2acSGuenter Roeck 	{ .temp_c	= -5, .ohm	= 166623 },
118088ce2acSGuenter Roeck 	{ .temp_c	= 0, .ohm	= 124850 },
119088ce2acSGuenter Roeck 	{ .temp_c	= 5, .ohm	= 94287 },
120088ce2acSGuenter Roeck 	{ .temp_c	= 10, .ohm	= 71747 },
121088ce2acSGuenter Roeck 	{ .temp_c	= 15, .ohm	= 54996 },
122088ce2acSGuenter Roeck 	{ .temp_c	= 20, .ohm	= 42455 },
123088ce2acSGuenter Roeck 	{ .temp_c	= 25, .ohm	= 33000 },
124088ce2acSGuenter Roeck 	{ .temp_c	= 30, .ohm	= 25822 },
125088ce2acSGuenter Roeck 	{ .temp_c	= 35, .ohm	= 20335 },
126088ce2acSGuenter Roeck 	{ .temp_c	= 40, .ohm	= 16115 },
127088ce2acSGuenter Roeck 	{ .temp_c	= 45, .ohm	= 12849 },
128088ce2acSGuenter Roeck 	{ .temp_c	= 50, .ohm	= 10306 },
129088ce2acSGuenter Roeck 	{ .temp_c	= 55, .ohm	= 8314 },
130088ce2acSGuenter Roeck 	{ .temp_c	= 60, .ohm	= 6746 },
131088ce2acSGuenter Roeck 	{ .temp_c	= 65, .ohm	= 5503 },
132088ce2acSGuenter Roeck 	{ .temp_c	= 70, .ohm	= 4513 },
133088ce2acSGuenter Roeck 	{ .temp_c	= 75, .ohm	= 3721 },
134088ce2acSGuenter Roeck 	{ .temp_c	= 80, .ohm	= 3084 },
135088ce2acSGuenter Roeck 	{ .temp_c	= 85, .ohm	= 2569 },
136088ce2acSGuenter Roeck 	{ .temp_c	= 90, .ohm	= 2151 },
137088ce2acSGuenter Roeck 	{ .temp_c	= 95, .ohm	= 1809 },
138088ce2acSGuenter Roeck 	{ .temp_c	= 100, .ohm	= 1529 },
139088ce2acSGuenter Roeck 	{ .temp_c	= 105, .ohm	= 1299 },
140088ce2acSGuenter Roeck 	{ .temp_c	= 110, .ohm	= 1108 },
141088ce2acSGuenter Roeck 	{ .temp_c	= 115, .ohm	= 949 },
142088ce2acSGuenter Roeck 	{ .temp_c	= 120, .ohm	= 817 },
143088ce2acSGuenter Roeck 	{ .temp_c	= 125, .ohm	= 707 },
144f22aaaa7SDonggeun Kim };
145f22aaaa7SDonggeun Kim 
146887ee434SBeomho Seo static const struct ntc_compensation ncpXXwf104[] = {
147887ee434SBeomho Seo 	{ .temp_c	= -40, .ohm	= 4397119 },
148887ee434SBeomho Seo 	{ .temp_c	= -35, .ohm	= 3088599 },
149887ee434SBeomho Seo 	{ .temp_c	= -30, .ohm	= 2197225 },
150887ee434SBeomho Seo 	{ .temp_c	= -25, .ohm	= 1581881 },
151887ee434SBeomho Seo 	{ .temp_c	= -20, .ohm	= 1151037 },
152887ee434SBeomho Seo 	{ .temp_c	= -15, .ohm	= 846579 },
153887ee434SBeomho Seo 	{ .temp_c	= -10, .ohm	= 628988 },
154887ee434SBeomho Seo 	{ .temp_c	= -5, .ohm	= 471632 },
155887ee434SBeomho Seo 	{ .temp_c	= 0, .ohm	= 357012 },
156887ee434SBeomho Seo 	{ .temp_c	= 5, .ohm	= 272500 },
157887ee434SBeomho Seo 	{ .temp_c	= 10, .ohm	= 209710 },
158887ee434SBeomho Seo 	{ .temp_c	= 15, .ohm	= 162651 },
159887ee434SBeomho Seo 	{ .temp_c	= 20, .ohm	= 127080 },
160887ee434SBeomho Seo 	{ .temp_c	= 25, .ohm	= 100000 },
161887ee434SBeomho Seo 	{ .temp_c	= 30, .ohm	= 79222 },
162887ee434SBeomho Seo 	{ .temp_c	= 35, .ohm	= 63167 },
163887ee434SBeomho Seo 	{ .temp_c	= 40, .ohm	= 50677 },
164887ee434SBeomho Seo 	{ .temp_c	= 45, .ohm	= 40904 },
165887ee434SBeomho Seo 	{ .temp_c	= 50, .ohm	= 33195 },
166887ee434SBeomho Seo 	{ .temp_c	= 55, .ohm	= 27091 },
167887ee434SBeomho Seo 	{ .temp_c	= 60, .ohm	= 22224 },
168887ee434SBeomho Seo 	{ .temp_c	= 65, .ohm	= 18323 },
169887ee434SBeomho Seo 	{ .temp_c	= 70, .ohm	= 15184 },
170887ee434SBeomho Seo 	{ .temp_c	= 75, .ohm	= 12635 },
171887ee434SBeomho Seo 	{ .temp_c	= 80, .ohm	= 10566 },
172887ee434SBeomho Seo 	{ .temp_c	= 85, .ohm	= 8873 },
173887ee434SBeomho Seo 	{ .temp_c	= 90, .ohm	= 7481 },
174887ee434SBeomho Seo 	{ .temp_c	= 95, .ohm	= 6337 },
175887ee434SBeomho Seo 	{ .temp_c	= 100, .ohm	= 5384 },
176887ee434SBeomho Seo 	{ .temp_c	= 105, .ohm	= 4594 },
177887ee434SBeomho Seo 	{ .temp_c	= 110, .ohm	= 3934 },
178887ee434SBeomho Seo 	{ .temp_c	= 115, .ohm	= 3380 },
179887ee434SBeomho Seo 	{ .temp_c	= 120, .ohm	= 2916 },
180887ee434SBeomho Seo 	{ .temp_c	= 125, .ohm	= 2522 },
181887ee434SBeomho Seo };
182887ee434SBeomho Seo 
18354ce3a0dSJoseph McNally static const struct ntc_compensation ncpXXxh103[] = {
184*b5cc1496SMaud Spierings 	{ .temp_c	= -40, .ohm	= 195652 },
185*b5cc1496SMaud Spierings 	{ .temp_c	= -35, .ohm	= 148171 },
186*b5cc1496SMaud Spierings 	{ .temp_c	= -30, .ohm	= 113347 },
187*b5cc1496SMaud Spierings 	{ .temp_c	= -25, .ohm	= 87559 },
188*b5cc1496SMaud Spierings 	{ .temp_c	= -20, .ohm	= 68237 },
189*b5cc1496SMaud Spierings 	{ .temp_c	= -15, .ohm	= 53650 },
190*b5cc1496SMaud Spierings 	{ .temp_c	= -10, .ohm	= 42506 },
191*b5cc1496SMaud Spierings 	{ .temp_c	= -5, .ohm	= 33892 },
192*b5cc1496SMaud Spierings 	{ .temp_c	= 0, .ohm	= 27219 },
193*b5cc1496SMaud Spierings 	{ .temp_c	= 5, .ohm	= 22021 },
194*b5cc1496SMaud Spierings 	{ .temp_c	= 10, .ohm	= 17926 },
195*b5cc1496SMaud Spierings 	{ .temp_c	= 15, .ohm	= 14674 },
196*b5cc1496SMaud Spierings 	{ .temp_c	= 20, .ohm	= 12081 },
19754ce3a0dSJoseph McNally 	{ .temp_c	= 25, .ohm	= 10000 },
198*b5cc1496SMaud Spierings 	{ .temp_c	= 30, .ohm	= 8315 },
199*b5cc1496SMaud Spierings 	{ .temp_c	= 35, .ohm	= 6948 },
200*b5cc1496SMaud Spierings 	{ .temp_c	= 40, .ohm	= 5834 },
201*b5cc1496SMaud Spierings 	{ .temp_c	= 45, .ohm	= 4917 },
202*b5cc1496SMaud Spierings 	{ .temp_c	= 50, .ohm	= 4161 },
203*b5cc1496SMaud Spierings 	{ .temp_c	= 55, .ohm	= 3535 },
204*b5cc1496SMaud Spierings 	{ .temp_c	= 60, .ohm	= 3014 },
205*b5cc1496SMaud Spierings 	{ .temp_c	= 65, .ohm	= 2586 },
206*b5cc1496SMaud Spierings 	{ .temp_c	= 70, .ohm	= 2228 },
207*b5cc1496SMaud Spierings 	{ .temp_c	= 75, .ohm	= 1925 },
208*b5cc1496SMaud Spierings 	{ .temp_c	= 80, .ohm	= 1669 },
209*b5cc1496SMaud Spierings 	{ .temp_c	= 85, .ohm	= 1452 },
210*b5cc1496SMaud Spierings 	{ .temp_c	= 90, .ohm	= 1268 },
211*b5cc1496SMaud Spierings 	{ .temp_c	= 95, .ohm	= 1110 },
212*b5cc1496SMaud Spierings 	{ .temp_c	= 100, .ohm	= 974 },
213*b5cc1496SMaud Spierings 	{ .temp_c	= 105, .ohm	= 858 },
214*b5cc1496SMaud Spierings 	{ .temp_c	= 110, .ohm	= 758 },
215*b5cc1496SMaud Spierings 	{ .temp_c	= 115, .ohm	= 672 },
216*b5cc1496SMaud Spierings 	{ .temp_c	= 120, .ohm	= 596 },
217*b5cc1496SMaud Spierings 	{ .temp_c	= 125, .ohm	= 531 },
21854ce3a0dSJoseph McNally };
21954ce3a0dSJoseph McNally 
220ed67f087SJohannes Pointner /*
221e8fda2c8SPeter Rosin  * The following compensation tables are from the specifications in EPCOS NTC
222e8fda2c8SPeter Rosin  * Thermistors Datasheets
223ed67f087SJohannes Pointner  */
224ed67f087SJohannes Pointner static const struct ntc_compensation b57330v2103[] = {
225ed67f087SJohannes Pointner 	{ .temp_c	= -40, .ohm	= 190030 },
226ed67f087SJohannes Pointner 	{ .temp_c	= -35, .ohm	= 145360 },
227ed67f087SJohannes Pointner 	{ .temp_c	= -30, .ohm	= 112060 },
228ed67f087SJohannes Pointner 	{ .temp_c	= -25, .ohm	= 87041 },
229ed67f087SJohannes Pointner 	{ .temp_c	= -20, .ohm	= 68104 },
230ed67f087SJohannes Pointner 	{ .temp_c	= -15, .ohm	= 53665 },
231ed67f087SJohannes Pointner 	{ .temp_c	= -10, .ohm	= 42576 },
232ed67f087SJohannes Pointner 	{ .temp_c	= -5, .ohm	= 34001 },
233ed67f087SJohannes Pointner 	{ .temp_c	= 0, .ohm	= 27326 },
234ed67f087SJohannes Pointner 	{ .temp_c	= 5, .ohm	= 22096 },
235ed67f087SJohannes Pointner 	{ .temp_c	= 10, .ohm	= 17973 },
236ed67f087SJohannes Pointner 	{ .temp_c	= 15, .ohm	= 14703 },
237ed67f087SJohannes Pointner 	{ .temp_c	= 20, .ohm	= 12090 },
238ed67f087SJohannes Pointner 	{ .temp_c	= 25, .ohm	= 10000 },
239ed67f087SJohannes Pointner 	{ .temp_c	= 30, .ohm	= 8311 },
240ed67f087SJohannes Pointner 	{ .temp_c	= 35, .ohm	= 6941 },
241ed67f087SJohannes Pointner 	{ .temp_c	= 40, .ohm	= 5825 },
242ed67f087SJohannes Pointner 	{ .temp_c	= 45, .ohm	= 4911 },
243ed67f087SJohannes Pointner 	{ .temp_c	= 50, .ohm	= 4158 },
244ed67f087SJohannes Pointner 	{ .temp_c	= 55, .ohm	= 3536 },
245ed67f087SJohannes Pointner 	{ .temp_c	= 60, .ohm	= 3019 },
246ed67f087SJohannes Pointner 	{ .temp_c	= 65, .ohm	= 2588 },
247ed67f087SJohannes Pointner 	{ .temp_c	= 70, .ohm	= 2227 },
248ed67f087SJohannes Pointner 	{ .temp_c	= 75, .ohm	= 1924 },
249ed67f087SJohannes Pointner 	{ .temp_c	= 80, .ohm	= 1668 },
250ed67f087SJohannes Pointner 	{ .temp_c	= 85, .ohm	= 1451 },
251ed67f087SJohannes Pointner 	{ .temp_c	= 90, .ohm	= 1266 },
252ed67f087SJohannes Pointner 	{ .temp_c	= 95, .ohm	= 1108 },
253ed67f087SJohannes Pointner 	{ .temp_c	= 100, .ohm	= 973 },
254ed67f087SJohannes Pointner 	{ .temp_c	= 105, .ohm	= 857 },
255ed67f087SJohannes Pointner 	{ .temp_c	= 110, .ohm	= 757 },
256ed67f087SJohannes Pointner 	{ .temp_c	= 115, .ohm	= 671 },
257ed67f087SJohannes Pointner 	{ .temp_c	= 120, .ohm	= 596 },
258ed67f087SJohannes Pointner 	{ .temp_c	= 125, .ohm	= 531 },
259ed67f087SJohannes Pointner };
260ed67f087SJohannes Pointner 
261e8fda2c8SPeter Rosin static const struct ntc_compensation b57891s0103[] = {
262e8fda2c8SPeter Rosin 	{ .temp_c	= -55.0, .ohm	= 878900 },
263e8fda2c8SPeter Rosin 	{ .temp_c	= -50.0, .ohm	= 617590 },
264e8fda2c8SPeter Rosin 	{ .temp_c	= -45.0, .ohm	= 439340 },
265e8fda2c8SPeter Rosin 	{ .temp_c	= -40.0, .ohm	= 316180 },
266e8fda2c8SPeter Rosin 	{ .temp_c	= -35.0, .ohm	= 230060 },
267e8fda2c8SPeter Rosin 	{ .temp_c	= -30.0, .ohm	= 169150 },
268e8fda2c8SPeter Rosin 	{ .temp_c	= -25.0, .ohm	= 125550 },
269e8fda2c8SPeter Rosin 	{ .temp_c	= -20.0, .ohm	= 94143 },
270e8fda2c8SPeter Rosin 	{ .temp_c	= -15.0, .ohm	= 71172 },
271e8fda2c8SPeter Rosin 	{ .temp_c	= -10.0, .ohm	= 54308 },
272e8fda2c8SPeter Rosin 	{ .temp_c	= -5.0, .ohm	= 41505 },
273e8fda2c8SPeter Rosin 	{ .temp_c	= 0.0, .ohm	= 32014 },
274e8fda2c8SPeter Rosin 	{ .temp_c	= 5.0, .ohm	= 25011 },
275e8fda2c8SPeter Rosin 	{ .temp_c	= 10.0, .ohm	= 19691 },
276e8fda2c8SPeter Rosin 	{ .temp_c	= 15.0, .ohm	= 15618 },
277e8fda2c8SPeter Rosin 	{ .temp_c	= 20.0, .ohm	= 12474 },
278e8fda2c8SPeter Rosin 	{ .temp_c	= 25.0, .ohm	= 10000 },
279e8fda2c8SPeter Rosin 	{ .temp_c	= 30.0, .ohm	= 8080 },
280e8fda2c8SPeter Rosin 	{ .temp_c	= 35.0, .ohm	= 6569 },
281e8fda2c8SPeter Rosin 	{ .temp_c	= 40.0, .ohm	= 5372 },
282e8fda2c8SPeter Rosin 	{ .temp_c	= 45.0, .ohm	= 4424 },
283e8fda2c8SPeter Rosin 	{ .temp_c	= 50.0, .ohm	= 3661 },
284e8fda2c8SPeter Rosin 	{ .temp_c	= 55.0, .ohm	= 3039 },
285e8fda2c8SPeter Rosin 	{ .temp_c	= 60.0, .ohm	= 2536 },
286e8fda2c8SPeter Rosin 	{ .temp_c	= 65.0, .ohm	= 2128 },
287e8fda2c8SPeter Rosin 	{ .temp_c	= 70.0, .ohm	= 1794 },
288e8fda2c8SPeter Rosin 	{ .temp_c	= 75.0, .ohm	= 1518 },
289e8fda2c8SPeter Rosin 	{ .temp_c	= 80.0, .ohm	= 1290 },
290e8fda2c8SPeter Rosin 	{ .temp_c	= 85.0, .ohm	= 1100 },
291e8fda2c8SPeter Rosin 	{ .temp_c	= 90.0, .ohm	= 942 },
292e8fda2c8SPeter Rosin 	{ .temp_c	= 95.0, .ohm	= 809 },
293e8fda2c8SPeter Rosin 	{ .temp_c	= 100.0, .ohm	= 697 },
294e8fda2c8SPeter Rosin 	{ .temp_c	= 105.0, .ohm	= 604 },
295e8fda2c8SPeter Rosin 	{ .temp_c	= 110.0, .ohm	= 525 },
296e8fda2c8SPeter Rosin 	{ .temp_c	= 115.0, .ohm	= 457 },
297e8fda2c8SPeter Rosin 	{ .temp_c	= 120.0, .ohm	= 400 },
298e8fda2c8SPeter Rosin 	{ .temp_c	= 125.0, .ohm	= 351 },
299e8fda2c8SPeter Rosin 	{ .temp_c	= 130.0, .ohm	= 308 },
300e8fda2c8SPeter Rosin 	{ .temp_c	= 135.0, .ohm	= 272 },
301e8fda2c8SPeter Rosin 	{ .temp_c	= 140.0, .ohm	= 240 },
302e8fda2c8SPeter Rosin 	{ .temp_c	= 145.0, .ohm	= 213 },
303e8fda2c8SPeter Rosin 	{ .temp_c	= 150.0, .ohm	= 189 },
304e8fda2c8SPeter Rosin 	{ .temp_c	= 155.0, .ohm	= 168 },
305e8fda2c8SPeter Rosin };
306e8fda2c8SPeter Rosin 
307737c086eSPeter Rosin struct ntc_type {
308737c086eSPeter Rosin 	const struct ntc_compensation *comp;
309737c086eSPeter Rosin 	int n_comp;
310737c086eSPeter Rosin };
311737c086eSPeter Rosin 
312737c086eSPeter Rosin #define NTC_TYPE(ntc, compensation) \
313737c086eSPeter Rosin [(ntc)] = { .comp = (compensation), .n_comp = ARRAY_SIZE(compensation) }
314737c086eSPeter Rosin 
315737c086eSPeter Rosin static const struct ntc_type ntc_type[] = {
316737c086eSPeter Rosin 	NTC_TYPE(TYPE_B57330V2103, b57330v2103),
317737c086eSPeter Rosin 	NTC_TYPE(TYPE_B57891S0103, b57891s0103),
318737c086eSPeter Rosin 	NTC_TYPE(TYPE_NCPXXWB473,  ncpXXwb473),
319737c086eSPeter Rosin 	NTC_TYPE(TYPE_NCPXXWF104,  ncpXXwf104),
320737c086eSPeter Rosin 	NTC_TYPE(TYPE_NCPXXWL333,  ncpXXwl333),
321737c086eSPeter Rosin 	NTC_TYPE(TYPE_NCPXXXH103,  ncpXXxh103),
322737c086eSPeter Rosin };
323737c086eSPeter Rosin 
324e380095bSLinus Walleij /*
325e380095bSLinus Walleij  * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required.
326e380095bSLinus Walleij  *
327e380095bSLinus Walleij  * How to setup pullup_ohm, pulldown_ohm, and connect is
328e380095bSLinus Walleij  * described at Documentation/hwmon/ntc_thermistor.rst
329e380095bSLinus Walleij  *
330e380095bSLinus Walleij  * pullup/down_ohm: 0 for infinite / not-connected
331e380095bSLinus Walleij  *
332e380095bSLinus Walleij  * chan: iio_channel pointer to communicate with the ADC which the
333e380095bSLinus Walleij  * thermistor is using for conversion of the analog values.
334e380095bSLinus Walleij  */
335f22aaaa7SDonggeun Kim struct ntc_data {
336f22aaaa7SDonggeun Kim 	const struct ntc_compensation *comp;
337f22aaaa7SDonggeun Kim 	int n_comp;
338e380095bSLinus Walleij 	unsigned int pullup_uv;
339e380095bSLinus Walleij 	unsigned int pullup_ohm;
340e380095bSLinus Walleij 	unsigned int pulldown_ohm;
341e380095bSLinus Walleij 	enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect;
342e380095bSLinus Walleij 	struct iio_channel *chan;
343f22aaaa7SDonggeun Kim };
344f22aaaa7SDonggeun Kim 
ntc_adc_iio_read(struct ntc_data * data)345e380095bSLinus Walleij static int ntc_adc_iio_read(struct ntc_data *data)
3469e8269deSNaveen Krishna Chatradhi {
347e380095bSLinus Walleij 	struct iio_channel *channel = data->chan;
3484f2d9cceSLinus Walleij 	int uv, ret;
3499e8269deSNaveen Krishna Chatradhi 
3504f2d9cceSLinus Walleij 	ret = iio_read_channel_processed_scale(channel, &uv, 1000);
3514f2d9cceSLinus Walleij 	if (ret < 0) {
3524f2d9cceSLinus Walleij 		int raw;
3534f2d9cceSLinus Walleij 
3544f2d9cceSLinus Walleij 		/*
3554f2d9cceSLinus Walleij 		 * This fallback uses a raw read and then
3564f2d9cceSLinus Walleij 		 * assumes the ADC is 12 bits, scaling with
3574f2d9cceSLinus Walleij 		 * a factor 1000 to get to microvolts.
3584f2d9cceSLinus Walleij 		 */
3590315253bSChris Lesiak 		ret = iio_read_channel_raw(channel, &raw);
3609e8269deSNaveen Krishna Chatradhi 		if (ret < 0) {
3619e8269deSNaveen Krishna Chatradhi 			pr_err("read channel() error: %d\n", ret);
3629e8269deSNaveen Krishna Chatradhi 			return ret;
3639e8269deSNaveen Krishna Chatradhi 		}
3640315253bSChris Lesiak 		ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000);
3650315253bSChris Lesiak 		if (ret < 0) {
3660315253bSChris Lesiak 			/* Assume 12 bit ADC with vref at pullup_uv */
367e380095bSLinus Walleij 			uv = (data->pullup_uv * (s64)raw) >> 12;
3680315253bSChris Lesiak 		}
3694f2d9cceSLinus Walleij 	}
3709e8269deSNaveen Krishna Chatradhi 
3710315253bSChris Lesiak 	return uv;
3729e8269deSNaveen Krishna Chatradhi }
3739e8269deSNaveen Krishna Chatradhi 
div64_u64_safe(u64 dividend,u64 divisor)374f22aaaa7SDonggeun Kim static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
375f22aaaa7SDonggeun Kim {
376f22aaaa7SDonggeun Kim 	if (divisor == 0 && dividend == 0)
377f22aaaa7SDonggeun Kim 		return 0;
378f22aaaa7SDonggeun Kim 	if (divisor == 0)
379f22aaaa7SDonggeun Kim 		return UINT_MAX;
380f22aaaa7SDonggeun Kim 	return div64_u64(dividend, divisor);
381f22aaaa7SDonggeun Kim }
382f22aaaa7SDonggeun Kim 
get_ohm_of_thermistor(struct ntc_data * data,unsigned int uv)383088ce2acSGuenter Roeck static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
384f22aaaa7SDonggeun Kim {
385e380095bSLinus Walleij 	u32 puv = data->pullup_uv;
386088ce2acSGuenter Roeck 	u64 n, puo, pdo;
387e380095bSLinus Walleij 	puo = data->pullup_ohm;
388e380095bSLinus Walleij 	pdo = data->pulldown_ohm;
389f22aaaa7SDonggeun Kim 
390f6725ae2SChris Lesiak 	if (uv == 0)
391e380095bSLinus Walleij 		return (data->connect == NTC_CONNECTED_POSITIVE) ?
392f6725ae2SChris Lesiak 			INT_MAX : 0;
393f6725ae2SChris Lesiak 	if (uv >= puv)
394e380095bSLinus Walleij 		return (data->connect == NTC_CONNECTED_POSITIVE) ?
395dbe43a62SGuenter Roeck 			0 : INT_MAX;
396f22aaaa7SDonggeun Kim 
397e380095bSLinus Walleij 	if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0)
398f6725ae2SChris Lesiak 		n = div_u64(pdo * (puv - uv), uv);
399e380095bSLinus Walleij 	else if (data->connect == NTC_CONNECTED_GROUND && pdo == 0)
400f6725ae2SChris Lesiak 		n = div_u64(puo * uv, puv - uv);
401e380095bSLinus Walleij 	else if (data->connect == NTC_CONNECTED_POSITIVE)
402f6725ae2SChris Lesiak 		n = div64_u64_safe(pdo * puo * (puv - uv),
403f6725ae2SChris Lesiak 				puo * uv - pdo * (puv - uv));
404f22aaaa7SDonggeun Kim 	else
405f6725ae2SChris Lesiak 		n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
406f22aaaa7SDonggeun Kim 
407088ce2acSGuenter Roeck 	if (n > INT_MAX)
408088ce2acSGuenter Roeck 		n = INT_MAX;
409088ce2acSGuenter Roeck 	return n;
410f22aaaa7SDonggeun Kim }
411f22aaaa7SDonggeun Kim 
lookup_comp(struct ntc_data * data,unsigned int ohm,int * i_low,int * i_high)412dbe43a62SGuenter Roeck static void lookup_comp(struct ntc_data *data, unsigned int ohm,
413dbe43a62SGuenter Roeck 			int *i_low, int *i_high)
414f22aaaa7SDonggeun Kim {
415dbe43a62SGuenter Roeck 	int start, end, mid;
416dbe43a62SGuenter Roeck 
417dbe43a62SGuenter Roeck 	/*
418dbe43a62SGuenter Roeck 	 * Handle special cases: Resistance is higher than or equal to
419dbe43a62SGuenter Roeck 	 * resistance in first table entry, or resistance is lower or equal
420dbe43a62SGuenter Roeck 	 * to resistance in last table entry.
421dbe43a62SGuenter Roeck 	 * In these cases, return i_low == i_high, either pointing to the
422dbe43a62SGuenter Roeck 	 * beginning or to the end of the table depending on the condition.
423dbe43a62SGuenter Roeck 	 */
424dbe43a62SGuenter Roeck 	if (ohm >= data->comp[0].ohm) {
425dbe43a62SGuenter Roeck 		*i_low = 0;
426dbe43a62SGuenter Roeck 		*i_high = 0;
427dbe43a62SGuenter Roeck 		return;
428dbe43a62SGuenter Roeck 	}
429dbe43a62SGuenter Roeck 	if (ohm <= data->comp[data->n_comp - 1].ohm) {
430dbe43a62SGuenter Roeck 		*i_low = data->n_comp - 1;
431dbe43a62SGuenter Roeck 		*i_high = data->n_comp - 1;
432dbe43a62SGuenter Roeck 		return;
433dbe43a62SGuenter Roeck 	}
434f22aaaa7SDonggeun Kim 
435f22aaaa7SDonggeun Kim 	/* Do a binary search on compensation table */
436f22aaaa7SDonggeun Kim 	start = 0;
437f22aaaa7SDonggeun Kim 	end = data->n_comp;
438dbe43a62SGuenter Roeck 	while (start < end) {
439f22aaaa7SDonggeun Kim 		mid = start + (end - start) / 2;
440dbe43a62SGuenter Roeck 		/*
441dbe43a62SGuenter Roeck 		 * start <= mid < end
442dbe43a62SGuenter Roeck 		 * data->comp[start].ohm > ohm >= data->comp[end].ohm
443dbe43a62SGuenter Roeck 		 *
444dbe43a62SGuenter Roeck 		 * We could check for "ohm == data->comp[mid].ohm" here, but
445dbe43a62SGuenter Roeck 		 * that is a quite unlikely condition, and we would have to
446dbe43a62SGuenter Roeck 		 * check again after updating start. Check it at the end instead
447dbe43a62SGuenter Roeck 		 * for simplicity.
448dbe43a62SGuenter Roeck 		 */
449dbe43a62SGuenter Roeck 		if (ohm >= data->comp[mid].ohm) {
450f22aaaa7SDonggeun Kim 			end = mid;
451dbe43a62SGuenter Roeck 		} else {
452f22aaaa7SDonggeun Kim 			start = mid + 1;
453dbe43a62SGuenter Roeck 			/*
454dbe43a62SGuenter Roeck 			 * ohm >= data->comp[start].ohm might be true here,
455dbe43a62SGuenter Roeck 			 * since we set start to mid + 1. In that case, we are
456dbe43a62SGuenter Roeck 			 * done. We could keep going, but the condition is quite
457dbe43a62SGuenter Roeck 			 * likely to occur, so it is worth checking for it.
458dbe43a62SGuenter Roeck 			 */
459dbe43a62SGuenter Roeck 			if (ohm >= data->comp[start].ohm)
460dbe43a62SGuenter Roeck 				end = start;
461dbe43a62SGuenter Roeck 		}
462dbe43a62SGuenter Roeck 		/*
463dbe43a62SGuenter Roeck 		 * start <= end
464dbe43a62SGuenter Roeck 		 * data->comp[start].ohm >= ohm >= data->comp[end].ohm
465dbe43a62SGuenter Roeck 		 */
466dbe43a62SGuenter Roeck 	}
467dbe43a62SGuenter Roeck 	/*
468dbe43a62SGuenter Roeck 	 * start == end
469dbe43a62SGuenter Roeck 	 * ohm >= data->comp[end].ohm
470dbe43a62SGuenter Roeck 	 */
471dbe43a62SGuenter Roeck 	*i_low = end;
472dbe43a62SGuenter Roeck 	if (ohm == data->comp[end].ohm)
473dbe43a62SGuenter Roeck 		*i_high = end;
474f22aaaa7SDonggeun Kim 	else
475dbe43a62SGuenter Roeck 		*i_high = end - 1;
476f22aaaa7SDonggeun Kim }
477f22aaaa7SDonggeun Kim 
get_temp_mc(struct ntc_data * data,unsigned int ohm)478088ce2acSGuenter Roeck static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
479f22aaaa7SDonggeun Kim {
480f22aaaa7SDonggeun Kim 	int low, high;
481dbe43a62SGuenter Roeck 	int temp;
482f22aaaa7SDonggeun Kim 
483dbe43a62SGuenter Roeck 	lookup_comp(data, ohm, &low, &high);
484bd56c1e9SLinus Walleij 	/*
485bd56c1e9SLinus Walleij 	 * First multiplying the table temperatures with 1000 to get to
486bd56c1e9SLinus Walleij 	 * millicentigrades (which is what we want) and then interpolating
487bd56c1e9SLinus Walleij 	 * will give the best precision.
488bd56c1e9SLinus Walleij 	 */
489bd56c1e9SLinus Walleij 	temp = fixp_linear_interpolate(data->comp[low].ohm,
490bd56c1e9SLinus Walleij 				       data->comp[low].temp_c * 1000,
491bd56c1e9SLinus Walleij 				       data->comp[high].ohm,
492bd56c1e9SLinus Walleij 				       data->comp[high].temp_c * 1000,
493bd56c1e9SLinus Walleij 				       ohm);
494dbe43a62SGuenter Roeck 	return temp;
495f22aaaa7SDonggeun Kim }
496f22aaaa7SDonggeun Kim 
ntc_thermistor_get_ohm(struct ntc_data * data)497dbe43a62SGuenter Roeck static int ntc_thermistor_get_ohm(struct ntc_data *data)
498f22aaaa7SDonggeun Kim {
499088ce2acSGuenter Roeck 	int read_uv;
500f22aaaa7SDonggeun Kim 
501e380095bSLinus Walleij 	read_uv = ntc_adc_iio_read(data);
502088ce2acSGuenter Roeck 	if (read_uv < 0)
503088ce2acSGuenter Roeck 		return read_uv;
504088ce2acSGuenter Roeck 	return get_ohm_of_thermistor(data, read_uv);
505f22aaaa7SDonggeun Kim }
506f22aaaa7SDonggeun Kim 
ntc_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)5077cc7de93SGuenter Roeck static int ntc_read(struct device *dev, enum hwmon_sensor_types type,
5087cc7de93SGuenter Roeck 		    u32 attr, int channel, long *val)
509f22aaaa7SDonggeun Kim {
510f22aaaa7SDonggeun Kim 	struct ntc_data *data = dev_get_drvdata(dev);
511dbe43a62SGuenter Roeck 	int ohm;
512f22aaaa7SDonggeun Kim 
5137cc7de93SGuenter Roeck 	switch (type) {
5147cc7de93SGuenter Roeck 	case hwmon_temp:
5157cc7de93SGuenter Roeck 		switch (attr) {
5167cc7de93SGuenter Roeck 		case hwmon_temp_input:
517dbe43a62SGuenter Roeck 			ohm = ntc_thermistor_get_ohm(data);
518dbe43a62SGuenter Roeck 			if (ohm < 0)
519dbe43a62SGuenter Roeck 				return ohm;
5207cc7de93SGuenter Roeck 			*val = get_temp_mc(data, ohm);
5217cc7de93SGuenter Roeck 			return 0;
5227cc7de93SGuenter Roeck 		case hwmon_temp_type:
5237cc7de93SGuenter Roeck 			*val = 4;
5247cc7de93SGuenter Roeck 			return 0;
5257cc7de93SGuenter Roeck 		default:
5267cc7de93SGuenter Roeck 			break;
5277cc7de93SGuenter Roeck 		}
5287cc7de93SGuenter Roeck 		break;
5297cc7de93SGuenter Roeck 	default:
5307cc7de93SGuenter Roeck 		break;
5317cc7de93SGuenter Roeck 	}
5327cc7de93SGuenter Roeck 	return -EINVAL;
533f22aaaa7SDonggeun Kim }
534f22aaaa7SDonggeun Kim 
ntc_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)5357cc7de93SGuenter Roeck static umode_t ntc_is_visible(const void *data, enum hwmon_sensor_types type,
5367cc7de93SGuenter Roeck 			      u32 attr, int channel)
5377cc7de93SGuenter Roeck {
5387cc7de93SGuenter Roeck 	if (type == hwmon_temp) {
5397cc7de93SGuenter Roeck 		switch (attr) {
5407cc7de93SGuenter Roeck 		case hwmon_temp_input:
5417cc7de93SGuenter Roeck 		case hwmon_temp_type:
5427cc7de93SGuenter Roeck 			return 0444;
5437cc7de93SGuenter Roeck 		default:
5447cc7de93SGuenter Roeck 			break;
5457cc7de93SGuenter Roeck 		}
5467cc7de93SGuenter Roeck 	}
5477cc7de93SGuenter Roeck 	return 0;
5487cc7de93SGuenter Roeck }
549f22aaaa7SDonggeun Kim 
5509add51f2SKrzysztof Kozlowski static const struct hwmon_channel_info * const ntc_info[] = {
5510ddca577SGuenter Roeck 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
5520ddca577SGuenter Roeck 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_TYPE),
5537cc7de93SGuenter Roeck 	NULL
5547cc7de93SGuenter Roeck };
5557cc7de93SGuenter Roeck 
5567cc7de93SGuenter Roeck static const struct hwmon_ops ntc_hwmon_ops = {
5577cc7de93SGuenter Roeck 	.is_visible = ntc_is_visible,
5587cc7de93SGuenter Roeck 	.read = ntc_read,
5597cc7de93SGuenter Roeck };
5607cc7de93SGuenter Roeck 
5617cc7de93SGuenter Roeck static const struct hwmon_chip_info ntc_chip_info = {
5627cc7de93SGuenter Roeck 	.ops = &ntc_hwmon_ops,
5637cc7de93SGuenter Roeck 	.info = ntc_info,
5642251aef6SEduardo Valentin };
5652251aef6SEduardo Valentin 
ntc_thermistor_parse_props(struct device * dev,struct ntc_data * data)56670760e80SLinus Walleij static int ntc_thermistor_parse_props(struct device *dev,
567e0149eebSLinus Walleij 				      struct ntc_data *data)
568e0149eebSLinus Walleij {
569e0149eebSLinus Walleij 	struct iio_channel *chan;
570e0149eebSLinus Walleij 	enum iio_chan_type type;
571e0149eebSLinus Walleij 	int ret;
572e0149eebSLinus Walleij 
573e0149eebSLinus Walleij 	chan = devm_iio_channel_get(dev, NULL);
574e0149eebSLinus Walleij 	if (IS_ERR(chan))
575e0149eebSLinus Walleij 		return PTR_ERR(chan);
576e0149eebSLinus Walleij 
577e0149eebSLinus Walleij 	ret = iio_get_channel_type(chan, &type);
578e0149eebSLinus Walleij 	if (ret < 0)
579e0149eebSLinus Walleij 		return ret;
580e0149eebSLinus Walleij 
581e0149eebSLinus Walleij 	if (type != IIO_VOLTAGE)
582e0149eebSLinus Walleij 		return -EINVAL;
583e0149eebSLinus Walleij 
58470760e80SLinus Walleij 	ret = device_property_read_u32(dev, "pullup-uv", &data->pullup_uv);
58570760e80SLinus Walleij 	if (ret)
58670760e80SLinus Walleij 		return dev_err_probe(dev,  ret, "pullup-uv not specified\n");
587e0149eebSLinus Walleij 
58870760e80SLinus Walleij 	ret = device_property_read_u32(dev, "pullup-ohm", &data->pullup_ohm);
58970760e80SLinus Walleij 	if (ret)
59070760e80SLinus Walleij 		return dev_err_probe(dev,  ret, "pullup-ohm not specified\n");
59170760e80SLinus Walleij 
59270760e80SLinus Walleij 	ret = device_property_read_u32(dev, "pulldown-ohm", &data->pulldown_ohm);
59370760e80SLinus Walleij 	if (ret)
59470760e80SLinus Walleij 		return dev_err_probe(dev,  ret, "pulldown-ohm not specified\n");
59570760e80SLinus Walleij 
59670760e80SLinus Walleij 	if (device_property_read_bool(dev, "connected-positive"))
597e0149eebSLinus Walleij 		data->connect = NTC_CONNECTED_POSITIVE;
598e0149eebSLinus Walleij 	else /* status change should be possible if not always on. */
599e0149eebSLinus Walleij 		data->connect = NTC_CONNECTED_GROUND;
600e0149eebSLinus Walleij 
601e0149eebSLinus Walleij 	data->chan = chan;
602e0149eebSLinus Walleij 
603e0149eebSLinus Walleij 	return 0;
604e0149eebSLinus Walleij }
605e0149eebSLinus Walleij 
ntc_thermistor_probe(struct platform_device * pdev)6066c931ae1SBill Pemberton static int ntc_thermistor_probe(struct platform_device *pdev)
607f22aaaa7SDonggeun Kim {
60848001525SGuenter Roeck 	struct device *dev = &pdev->dev;
6099e8269deSNaveen Krishna Chatradhi 	const struct platform_device_id *pdev_id;
6105e7f5994SGuenter Roeck 	struct device *hwmon_dev;
611f22aaaa7SDonggeun Kim 	struct ntc_data *data;
612e0149eebSLinus Walleij 	int ret;
6139e8269deSNaveen Krishna Chatradhi 
614e0149eebSLinus Walleij 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
615e0149eebSLinus Walleij 	if (!data)
616e0149eebSLinus Walleij 		return -ENOMEM;
617f22aaaa7SDonggeun Kim 
61870760e80SLinus Walleij 	ret = ntc_thermistor_parse_props(dev, data);
619e0149eebSLinus Walleij 	if (ret)
620e0149eebSLinus Walleij 		return ret;
621f22aaaa7SDonggeun Kim 
622e380095bSLinus Walleij 	if (data->pullup_uv == 0 ||
623e380095bSLinus Walleij 	    (data->pullup_ohm == 0 && data->connect ==
624f22aaaa7SDonggeun Kim 	     NTC_CONNECTED_GROUND) ||
625e380095bSLinus Walleij 	    (data->pulldown_ohm == 0 && data->connect ==
626f22aaaa7SDonggeun Kim 	     NTC_CONNECTED_POSITIVE) ||
627e380095bSLinus Walleij 	    (data->connect != NTC_CONNECTED_POSITIVE &&
628e380095bSLinus Walleij 	     data->connect != NTC_CONNECTED_GROUND)) {
629209218efSLinus Walleij 		dev_err(dev, "Required data to use NTC driver not supplied.\n");
630f22aaaa7SDonggeun Kim 		return -EINVAL;
631f22aaaa7SDonggeun Kim 	}
632f22aaaa7SDonggeun Kim 
63370760e80SLinus Walleij 	pdev_id = device_get_match_data(dev);
6349e8269deSNaveen Krishna Chatradhi 
635737c086eSPeter Rosin 	if (pdev_id->driver_data >= ARRAY_SIZE(ntc_type)) {
63648001525SGuenter Roeck 		dev_err(dev, "Unknown device type: %lu(%s)\n",
6379e8269deSNaveen Krishna Chatradhi 				pdev_id->driver_data, pdev_id->name);
63841141e64SGuenter Roeck 		return -EINVAL;
639f22aaaa7SDonggeun Kim 	}
640f22aaaa7SDonggeun Kim 
641737c086eSPeter Rosin 	data->comp   = ntc_type[pdev_id->driver_data].comp;
642737c086eSPeter Rosin 	data->n_comp = ntc_type[pdev_id->driver_data].n_comp;
643737c086eSPeter Rosin 
6447cc7de93SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev_id->name,
6457cc7de93SGuenter Roeck 							 data, &ntc_chip_info,
6467cc7de93SGuenter Roeck 							 NULL);
6475e7f5994SGuenter Roeck 	if (IS_ERR(hwmon_dev)) {
64848001525SGuenter Roeck 		dev_err(dev, "unable to register as hwmon device.\n");
6495e7f5994SGuenter Roeck 		return PTR_ERR(hwmon_dev);
650f22aaaa7SDonggeun Kim 	}
651f22aaaa7SDonggeun Kim 
65248001525SGuenter Roeck 	dev_info(dev, "Thermistor type: %s successfully probed.\n",
65393a88ef3SNaveen Krishna Chatradhi 		 pdev_id->name);
6549e8269deSNaveen Krishna Chatradhi 
655f22aaaa7SDonggeun Kim 	return 0;
656f22aaaa7SDonggeun Kim }
657f22aaaa7SDonggeun Kim 
6589f448e79SLinus Walleij static const struct of_device_id ntc_match[] = {
6599f448e79SLinus Walleij 	{ .compatible = "epcos,b57330v2103",
6609f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_B57330V2103]},
6619f448e79SLinus Walleij 	{ .compatible = "epcos,b57891s0103",
6629f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_B57891S0103] },
6639f448e79SLinus Walleij 	{ .compatible = "murata,ncp03wb473",
6649f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP03WB473] },
6659f448e79SLinus Walleij 	{ .compatible = "murata,ncp03wf104",
6669f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP03WF104] },
6679f448e79SLinus Walleij 	{ .compatible = "murata,ncp15wb473",
6689f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP15WB473] },
6699f448e79SLinus Walleij 	{ .compatible = "murata,ncp15wl333",
6709f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP15WL333] },
6719f448e79SLinus Walleij 	{ .compatible = "murata,ncp15xh103",
6729f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP15XH103] },
6739f448e79SLinus Walleij 	{ .compatible = "murata,ncp18wb473",
6749f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP18WB473] },
6759f448e79SLinus Walleij 	{ .compatible = "murata,ncp21wb473",
6769f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP21WB473] },
677e13e979bSLinus Walleij 	{ .compatible = "samsung,1404-001221",
678e13e979bSLinus Walleij 		.data = &ntc_thermistor_id[NTC_SSG1404001221] },
6799f448e79SLinus Walleij 
6809f448e79SLinus Walleij 	/* Usage of vendor name "ntc" is deprecated */
6819f448e79SLinus Walleij 	{ .compatible = "ntc,ncp03wb473",
6829f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP03WB473] },
6839f448e79SLinus Walleij 	{ .compatible = "ntc,ncp15wb473",
6849f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP15WB473] },
6859f448e79SLinus Walleij 	{ .compatible = "ntc,ncp15wl333",
6869f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP15WL333] },
6879f448e79SLinus Walleij 	{ .compatible = "ntc,ncp18wb473",
6889f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP18WB473] },
6899f448e79SLinus Walleij 	{ .compatible = "ntc,ncp21wb473",
6909f448e79SLinus Walleij 		.data = &ntc_thermistor_id[NTC_NCP21WB473] },
6919f448e79SLinus Walleij 	{ },
6929f448e79SLinus Walleij };
6939f448e79SLinus Walleij MODULE_DEVICE_TABLE(of, ntc_match);
6949f448e79SLinus Walleij 
695f22aaaa7SDonggeun Kim static struct platform_driver ntc_thermistor_driver = {
696f22aaaa7SDonggeun Kim 	.driver = {
697f22aaaa7SDonggeun Kim 		.name = "ntc-thermistor",
69870760e80SLinus Walleij 		.of_match_table = ntc_match,
699f22aaaa7SDonggeun Kim 	},
700f22aaaa7SDonggeun Kim 	.probe = ntc_thermistor_probe,
701f22aaaa7SDonggeun Kim 	.id_table = ntc_thermistor_id,
702f22aaaa7SDonggeun Kim };
703f22aaaa7SDonggeun Kim 
70425a236a5SAxel Lin module_platform_driver(ntc_thermistor_driver);
705f22aaaa7SDonggeun Kim 
706ed67f087SJohannes Pointner MODULE_DESCRIPTION("NTC Thermistor Driver");
707f22aaaa7SDonggeun Kim MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
708f22aaaa7SDonggeun Kim MODULE_LICENSE("GPL");
709f22aaaa7SDonggeun Kim MODULE_ALIAS("platform:ntc-thermistor");
710