1/*
2 * ntc_thermistor.c - NTC Thermistors
3 *
4 *  Copyright (C) 2010 Samsung Electronics
5 *  MyungJoo Ham <myungjoo.ham@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 */
22
23#include <linux/slab.h>
24#include <linux/module.h>
25#include <linux/pm_runtime.h>
26#include <linux/math64.h>
27#include <linux/platform_device.h>
28#include <linux/err.h>
29#include <linux/of.h>
30#include <linux/of_device.h>
31
32#include <linux/platform_data/ntc_thermistor.h>
33
34#include <linux/iio/iio.h>
35#include <linux/iio/machine.h>
36#include <linux/iio/driver.h>
37#include <linux/iio/consumer.h>
38
39#include <linux/hwmon.h>
40#include <linux/hwmon-sysfs.h>
41#include <linux/thermal.h>
42
43struct ntc_compensation {
44	int		temp_c;
45	unsigned int	ohm;
46};
47
48/* Order matters, ntc_match references the entries by index */
49static const struct platform_device_id ntc_thermistor_id[] = {
50	{ "ncp15wb473", TYPE_NCPXXWB473 },
51	{ "ncp18wb473", TYPE_NCPXXWB473 },
52	{ "ncp21wb473", TYPE_NCPXXWB473 },
53	{ "ncp03wb473", TYPE_NCPXXWB473 },
54	{ "ncp15wl333", TYPE_NCPXXWL333 },
55	{ "b57330v2103", TYPE_B57330V2103},
56	{ },
57};
58
59/*
60 * A compensation table should be sorted by the values of .ohm
61 * in descending order.
62 * The following compensation tables are from the specification of Murata NTC
63 * Thermistors Datasheet
64 */
65static const struct ntc_compensation ncpXXwb473[] = {
66	{ .temp_c	= -40, .ohm	= 1747920 },
67	{ .temp_c	= -35, .ohm	= 1245428 },
68	{ .temp_c	= -30, .ohm	= 898485 },
69	{ .temp_c	= -25, .ohm	= 655802 },
70	{ .temp_c	= -20, .ohm	= 483954 },
71	{ .temp_c	= -15, .ohm	= 360850 },
72	{ .temp_c	= -10, .ohm	= 271697 },
73	{ .temp_c	= -5, .ohm	= 206463 },
74	{ .temp_c	= 0, .ohm	= 158214 },
75	{ .temp_c	= 5, .ohm	= 122259 },
76	{ .temp_c	= 10, .ohm	= 95227 },
77	{ .temp_c	= 15, .ohm	= 74730 },
78	{ .temp_c	= 20, .ohm	= 59065 },
79	{ .temp_c	= 25, .ohm	= 47000 },
80	{ .temp_c	= 30, .ohm	= 37643 },
81	{ .temp_c	= 35, .ohm	= 30334 },
82	{ .temp_c	= 40, .ohm	= 24591 },
83	{ .temp_c	= 45, .ohm	= 20048 },
84	{ .temp_c	= 50, .ohm	= 16433 },
85	{ .temp_c	= 55, .ohm	= 13539 },
86	{ .temp_c	= 60, .ohm	= 11209 },
87	{ .temp_c	= 65, .ohm	= 9328 },
88	{ .temp_c	= 70, .ohm	= 7798 },
89	{ .temp_c	= 75, .ohm	= 6544 },
90	{ .temp_c	= 80, .ohm	= 5518 },
91	{ .temp_c	= 85, .ohm	= 4674 },
92	{ .temp_c	= 90, .ohm	= 3972 },
93	{ .temp_c	= 95, .ohm	= 3388 },
94	{ .temp_c	= 100, .ohm	= 2902 },
95	{ .temp_c	= 105, .ohm	= 2494 },
96	{ .temp_c	= 110, .ohm	= 2150 },
97	{ .temp_c	= 115, .ohm	= 1860 },
98	{ .temp_c	= 120, .ohm	= 1615 },
99	{ .temp_c	= 125, .ohm	= 1406 },
100};
101static const struct ntc_compensation ncpXXwl333[] = {
102	{ .temp_c	= -40, .ohm	= 1610154 },
103	{ .temp_c	= -35, .ohm	= 1130850 },
104	{ .temp_c	= -30, .ohm	= 802609 },
105	{ .temp_c	= -25, .ohm	= 575385 },
106	{ .temp_c	= -20, .ohm	= 416464 },
107	{ .temp_c	= -15, .ohm	= 304219 },
108	{ .temp_c	= -10, .ohm	= 224193 },
109	{ .temp_c	= -5, .ohm	= 166623 },
110	{ .temp_c	= 0, .ohm	= 124850 },
111	{ .temp_c	= 5, .ohm	= 94287 },
112	{ .temp_c	= 10, .ohm	= 71747 },
113	{ .temp_c	= 15, .ohm	= 54996 },
114	{ .temp_c	= 20, .ohm	= 42455 },
115	{ .temp_c	= 25, .ohm	= 33000 },
116	{ .temp_c	= 30, .ohm	= 25822 },
117	{ .temp_c	= 35, .ohm	= 20335 },
118	{ .temp_c	= 40, .ohm	= 16115 },
119	{ .temp_c	= 45, .ohm	= 12849 },
120	{ .temp_c	= 50, .ohm	= 10306 },
121	{ .temp_c	= 55, .ohm	= 8314 },
122	{ .temp_c	= 60, .ohm	= 6746 },
123	{ .temp_c	= 65, .ohm	= 5503 },
124	{ .temp_c	= 70, .ohm	= 4513 },
125	{ .temp_c	= 75, .ohm	= 3721 },
126	{ .temp_c	= 80, .ohm	= 3084 },
127	{ .temp_c	= 85, .ohm	= 2569 },
128	{ .temp_c	= 90, .ohm	= 2151 },
129	{ .temp_c	= 95, .ohm	= 1809 },
130	{ .temp_c	= 100, .ohm	= 1529 },
131	{ .temp_c	= 105, .ohm	= 1299 },
132	{ .temp_c	= 110, .ohm	= 1108 },
133	{ .temp_c	= 115, .ohm	= 949 },
134	{ .temp_c	= 120, .ohm	= 817 },
135	{ .temp_c	= 125, .ohm	= 707 },
136};
137
138/*
139 * The following compensation table is from the specification of EPCOS NTC
140 * Thermistors Datasheet
141 */
142static const struct ntc_compensation b57330v2103[] = {
143	{ .temp_c	= -40, .ohm	= 190030 },
144	{ .temp_c	= -35, .ohm	= 145360 },
145	{ .temp_c	= -30, .ohm	= 112060 },
146	{ .temp_c	= -25, .ohm	= 87041 },
147	{ .temp_c	= -20, .ohm	= 68104 },
148	{ .temp_c	= -15, .ohm	= 53665 },
149	{ .temp_c	= -10, .ohm	= 42576 },
150	{ .temp_c	= -5, .ohm	= 34001 },
151	{ .temp_c	= 0, .ohm	= 27326 },
152	{ .temp_c	= 5, .ohm	= 22096 },
153	{ .temp_c	= 10, .ohm	= 17973 },
154	{ .temp_c	= 15, .ohm	= 14703 },
155	{ .temp_c	= 20, .ohm	= 12090 },
156	{ .temp_c	= 25, .ohm	= 10000 },
157	{ .temp_c	= 30, .ohm	= 8311 },
158	{ .temp_c	= 35, .ohm	= 6941 },
159	{ .temp_c	= 40, .ohm	= 5825 },
160	{ .temp_c	= 45, .ohm	= 4911 },
161	{ .temp_c	= 50, .ohm	= 4158 },
162	{ .temp_c	= 55, .ohm	= 3536 },
163	{ .temp_c	= 60, .ohm	= 3019 },
164	{ .temp_c	= 65, .ohm	= 2588 },
165	{ .temp_c	= 70, .ohm	= 2227 },
166	{ .temp_c	= 75, .ohm	= 1924 },
167	{ .temp_c	= 80, .ohm	= 1668 },
168	{ .temp_c	= 85, .ohm	= 1451 },
169	{ .temp_c	= 90, .ohm	= 1266 },
170	{ .temp_c	= 95, .ohm	= 1108 },
171	{ .temp_c	= 100, .ohm	= 973 },
172	{ .temp_c	= 105, .ohm	= 857 },
173	{ .temp_c	= 110, .ohm	= 757 },
174	{ .temp_c	= 115, .ohm	= 671 },
175	{ .temp_c	= 120, .ohm	= 596 },
176	{ .temp_c	= 125, .ohm	= 531 },
177};
178
179struct ntc_data {
180	struct device *hwmon_dev;
181	struct ntc_thermistor_platform_data *pdata;
182	const struct ntc_compensation *comp;
183	struct device *dev;
184	int n_comp;
185	char name[PLATFORM_NAME_SIZE];
186	struct thermal_zone_device *tz;
187};
188
189#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
190static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
191{
192	struct iio_channel *channel = pdata->chan;
193	s64 result;
194	int val, ret;
195
196	ret = iio_read_channel_raw(channel, &val);
197	if (ret < 0) {
198		pr_err("read channel() error: %d\n", ret);
199		return ret;
200	}
201
202	/* unit: mV */
203	result = pdata->pullup_uv * (s64) val;
204	result >>= 12;
205
206	return (int)result;
207}
208
209static const struct of_device_id ntc_match[] = {
210	{ .compatible = "murata,ncp15wb473",
211		.data = &ntc_thermistor_id[0] },
212	{ .compatible = "murata,ncp18wb473",
213		.data = &ntc_thermistor_id[1] },
214	{ .compatible = "murata,ncp21wb473",
215		.data = &ntc_thermistor_id[2] },
216	{ .compatible = "murata,ncp03wb473",
217		.data = &ntc_thermistor_id[3] },
218	{ .compatible = "murata,ncp15wl333",
219		.data = &ntc_thermistor_id[4] },
220	{ .compatible = "epcos,b57330v2103",
221		.data = &ntc_thermistor_id[5]},
222
223	/* Usage of vendor name "ntc" is deprecated */
224	{ .compatible = "ntc,ncp15wb473",
225		.data = &ntc_thermistor_id[0] },
226	{ .compatible = "ntc,ncp18wb473",
227		.data = &ntc_thermistor_id[1] },
228	{ .compatible = "ntc,ncp21wb473",
229		.data = &ntc_thermistor_id[2] },
230	{ .compatible = "ntc,ncp03wb473",
231		.data = &ntc_thermistor_id[3] },
232	{ .compatible = "ntc,ncp15wl333",
233		.data = &ntc_thermistor_id[4] },
234	{ },
235};
236MODULE_DEVICE_TABLE(of, ntc_match);
237
238static struct ntc_thermistor_platform_data *
239ntc_thermistor_parse_dt(struct platform_device *pdev)
240{
241	struct iio_channel *chan;
242	enum iio_chan_type type;
243	struct device_node *np = pdev->dev.of_node;
244	struct ntc_thermistor_platform_data *pdata;
245	int ret;
246
247	if (!np)
248		return NULL;
249
250	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
251	if (!pdata)
252		return ERR_PTR(-ENOMEM);
253
254	chan = iio_channel_get(&pdev->dev, NULL);
255	if (IS_ERR(chan))
256		return ERR_CAST(chan);
257
258	ret = iio_get_channel_type(chan, &type);
259	if (ret < 0)
260		return ERR_PTR(ret);
261
262	if (type != IIO_VOLTAGE)
263		return ERR_PTR(-EINVAL);
264
265	if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv))
266		return ERR_PTR(-ENODEV);
267	if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm))
268		return ERR_PTR(-ENODEV);
269	if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm))
270		return ERR_PTR(-ENODEV);
271
272	if (of_find_property(np, "connected-positive", NULL))
273		pdata->connect = NTC_CONNECTED_POSITIVE;
274	else /* status change should be possible if not always on. */
275		pdata->connect = NTC_CONNECTED_GROUND;
276
277	pdata->chan = chan;
278	pdata->read_uv = ntc_adc_iio_read;
279
280	return pdata;
281}
282static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
283{
284	if (pdata->chan)
285		iio_channel_release(pdata->chan);
286}
287#else
288static struct ntc_thermistor_platform_data *
289ntc_thermistor_parse_dt(struct platform_device *pdev)
290{
291	return NULL;
292}
293
294#define ntc_match	NULL
295
296static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
297{ }
298#endif
299
300static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
301{
302	if (divisor == 0 && dividend == 0)
303		return 0;
304	if (divisor == 0)
305		return UINT_MAX;
306	return div64_u64(dividend, divisor);
307}
308
309static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
310{
311	struct ntc_thermistor_platform_data *pdata = data->pdata;
312	u64 mv = uv / 1000;
313	u64 pmv = pdata->pullup_uv / 1000;
314	u64 n, puo, pdo;
315	puo = pdata->pullup_ohm;
316	pdo = pdata->pulldown_ohm;
317
318	if (mv == 0) {
319		if (pdata->connect == NTC_CONNECTED_POSITIVE)
320			return INT_MAX;
321		return 0;
322	}
323	if (mv >= pmv)
324		return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
325			0 : INT_MAX;
326
327	if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
328		n = div64_u64_safe(pdo * (pmv - mv), mv);
329	else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
330		n = div64_u64_safe(puo * mv, pmv - mv);
331	else if (pdata->connect == NTC_CONNECTED_POSITIVE)
332		n = div64_u64_safe(pdo * puo * (pmv - mv),
333				puo * mv - pdo * (pmv - mv));
334	else
335		n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
336
337	if (n > INT_MAX)
338		n = INT_MAX;
339	return n;
340}
341
342static void lookup_comp(struct ntc_data *data, unsigned int ohm,
343			int *i_low, int *i_high)
344{
345	int start, end, mid;
346
347	/*
348	 * Handle special cases: Resistance is higher than or equal to
349	 * resistance in first table entry, or resistance is lower or equal
350	 * to resistance in last table entry.
351	 * In these cases, return i_low == i_high, either pointing to the
352	 * beginning or to the end of the table depending on the condition.
353	 */
354	if (ohm >= data->comp[0].ohm) {
355		*i_low = 0;
356		*i_high = 0;
357		return;
358	}
359	if (ohm <= data->comp[data->n_comp - 1].ohm) {
360		*i_low = data->n_comp - 1;
361		*i_high = data->n_comp - 1;
362		return;
363	}
364
365	/* Do a binary search on compensation table */
366	start = 0;
367	end = data->n_comp;
368	while (start < end) {
369		mid = start + (end - start) / 2;
370		/*
371		 * start <= mid < end
372		 * data->comp[start].ohm > ohm >= data->comp[end].ohm
373		 *
374		 * We could check for "ohm == data->comp[mid].ohm" here, but
375		 * that is a quite unlikely condition, and we would have to
376		 * check again after updating start. Check it at the end instead
377		 * for simplicity.
378		 */
379		if (ohm >= data->comp[mid].ohm) {
380			end = mid;
381		} else {
382			start = mid + 1;
383			/*
384			 * ohm >= data->comp[start].ohm might be true here,
385			 * since we set start to mid + 1. In that case, we are
386			 * done. We could keep going, but the condition is quite
387			 * likely to occur, so it is worth checking for it.
388			 */
389			if (ohm >= data->comp[start].ohm)
390				end = start;
391		}
392		/*
393		 * start <= end
394		 * data->comp[start].ohm >= ohm >= data->comp[end].ohm
395		 */
396	}
397	/*
398	 * start == end
399	 * ohm >= data->comp[end].ohm
400	 */
401	*i_low = end;
402	if (ohm == data->comp[end].ohm)
403		*i_high = end;
404	else
405		*i_high = end - 1;
406}
407
408static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
409{
410	int low, high;
411	int temp;
412
413	lookup_comp(data, ohm, &low, &high);
414	if (low == high) {
415		/* Unable to use linear approximation */
416		temp = data->comp[low].temp_c * 1000;
417	} else {
418		temp = data->comp[low].temp_c * 1000 +
419			((data->comp[high].temp_c - data->comp[low].temp_c) *
420			 1000 * ((int)ohm - (int)data->comp[low].ohm)) /
421			((int)data->comp[high].ohm - (int)data->comp[low].ohm);
422	}
423	return temp;
424}
425
426static int ntc_thermistor_get_ohm(struct ntc_data *data)
427{
428	int read_uv;
429
430	if (data->pdata->read_ohm)
431		return data->pdata->read_ohm();
432
433	if (data->pdata->read_uv) {
434		read_uv = data->pdata->read_uv(data->pdata);
435		if (read_uv < 0)
436			return read_uv;
437		return get_ohm_of_thermistor(data, read_uv);
438	}
439	return -EINVAL;
440}
441
442static int ntc_read_temp(void *dev, long *temp)
443{
444	struct ntc_data *data = dev_get_drvdata(dev);
445	int ohm;
446
447	ohm = ntc_thermistor_get_ohm(data);
448	if (ohm < 0)
449		return ohm;
450
451	*temp = get_temp_mc(data, ohm);
452
453	return 0;
454}
455
456static ssize_t ntc_show_name(struct device *dev,
457		struct device_attribute *attr, char *buf)
458{
459	struct ntc_data *data = dev_get_drvdata(dev);
460
461	return sprintf(buf, "%s\n", data->name);
462}
463
464static ssize_t ntc_show_type(struct device *dev,
465		struct device_attribute *attr, char *buf)
466{
467	return sprintf(buf, "4\n");
468}
469
470static ssize_t ntc_show_temp(struct device *dev,
471		struct device_attribute *attr, char *buf)
472{
473	struct ntc_data *data = dev_get_drvdata(dev);
474	int ohm;
475
476	ohm = ntc_thermistor_get_ohm(data);
477	if (ohm < 0)
478		return ohm;
479
480	return sprintf(buf, "%d\n", get_temp_mc(data, ohm));
481}
482
483static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
484static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
485static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
486
487static struct attribute *ntc_attributes[] = {
488	&dev_attr_name.attr,
489	&sensor_dev_attr_temp1_type.dev_attr.attr,
490	&sensor_dev_attr_temp1_input.dev_attr.attr,
491	NULL,
492};
493
494static const struct attribute_group ntc_attr_group = {
495	.attrs = ntc_attributes,
496};
497
498static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
499	.get_temp = ntc_read_temp,
500};
501
502static int ntc_thermistor_probe(struct platform_device *pdev)
503{
504	const struct of_device_id *of_id =
505			of_match_device(of_match_ptr(ntc_match), &pdev->dev);
506	const struct platform_device_id *pdev_id;
507	struct ntc_thermistor_platform_data *pdata;
508	struct ntc_data *data;
509	int ret;
510
511	pdata = ntc_thermistor_parse_dt(pdev);
512	if (IS_ERR(pdata))
513		return PTR_ERR(pdata);
514	else if (pdata == NULL)
515		pdata = dev_get_platdata(&pdev->dev);
516
517	if (!pdata) {
518		dev_err(&pdev->dev, "No platform init data supplied.\n");
519		return -ENODEV;
520	}
521
522	/* Either one of the two is required. */
523	if (!pdata->read_uv && !pdata->read_ohm) {
524		dev_err(&pdev->dev,
525			"Both read_uv and read_ohm missing. Need either one of the two.\n");
526		return -EINVAL;
527	}
528
529	if (pdata->read_uv && pdata->read_ohm) {
530		dev_warn(&pdev->dev,
531			 "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
532		pdata->read_uv = NULL;
533	}
534
535	if (pdata->read_uv && (pdata->pullup_uv == 0 ||
536				(pdata->pullup_ohm == 0 && pdata->connect ==
537				 NTC_CONNECTED_GROUND) ||
538				(pdata->pulldown_ohm == 0 && pdata->connect ==
539				 NTC_CONNECTED_POSITIVE) ||
540				(pdata->connect != NTC_CONNECTED_POSITIVE &&
541				 pdata->connect != NTC_CONNECTED_GROUND))) {
542		dev_err(&pdev->dev,
543			"Required data to use read_uv not supplied.\n");
544		return -EINVAL;
545	}
546
547	data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL);
548	if (!data)
549		return -ENOMEM;
550
551	pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
552
553	data->dev = &pdev->dev;
554	data->pdata = pdata;
555	strlcpy(data->name, pdev_id->name, sizeof(data->name));
556
557	switch (pdev_id->driver_data) {
558	case TYPE_NCPXXWB473:
559		data->comp = ncpXXwb473;
560		data->n_comp = ARRAY_SIZE(ncpXXwb473);
561		break;
562	case TYPE_NCPXXWL333:
563		data->comp = ncpXXwl333;
564		data->n_comp = ARRAY_SIZE(ncpXXwl333);
565		break;
566	case TYPE_B57330V2103:
567		data->comp = b57330v2103;
568		data->n_comp = ARRAY_SIZE(b57330v2103);
569		break;
570	default:
571		dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
572				pdev_id->driver_data, pdev_id->name);
573		return -EINVAL;
574	}
575
576	platform_set_drvdata(pdev, data);
577
578	ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
579	if (ret) {
580		dev_err(data->dev, "unable to create sysfs files\n");
581		return ret;
582	}
583
584	data->hwmon_dev = hwmon_device_register(data->dev);
585	if (IS_ERR(data->hwmon_dev)) {
586		dev_err(data->dev, "unable to register as hwmon device.\n");
587		ret = PTR_ERR(data->hwmon_dev);
588		goto err_after_sysfs;
589	}
590
591	dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
592								pdev_id->name);
593
594	data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev,
595						   &ntc_of_thermal_ops);
596	if (IS_ERR(data->tz)) {
597		dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
598		data->tz = NULL;
599	}
600
601	return 0;
602err_after_sysfs:
603	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
604	ntc_iio_channel_release(pdata);
605	return ret;
606}
607
608static int ntc_thermistor_remove(struct platform_device *pdev)
609{
610	struct ntc_data *data = platform_get_drvdata(pdev);
611	struct ntc_thermistor_platform_data *pdata = data->pdata;
612
613	hwmon_device_unregister(data->hwmon_dev);
614	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
615	ntc_iio_channel_release(pdata);
616
617	thermal_zone_of_sensor_unregister(data->dev, data->tz);
618
619	return 0;
620}
621
622static struct platform_driver ntc_thermistor_driver = {
623	.driver = {
624		.name = "ntc-thermistor",
625		.of_match_table = of_match_ptr(ntc_match),
626	},
627	.probe = ntc_thermistor_probe,
628	.remove = ntc_thermistor_remove,
629	.id_table = ntc_thermistor_id,
630};
631
632module_platform_driver(ntc_thermistor_driver);
633
634MODULE_DESCRIPTION("NTC Thermistor Driver");
635MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
636MODULE_LICENSE("GPL");
637MODULE_ALIAS("platform:ntc-thermistor");
638