xref: /linux/drivers/thermal/qcom/tsens.c (revision 9066073c6c27994a30187abf3b674770b4088348)
1*9066073cSRajendra Nayak /*
2*9066073cSRajendra Nayak  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3*9066073cSRajendra Nayak  *
4*9066073cSRajendra Nayak  * This program is free software; you can redistribute it and/or modify
5*9066073cSRajendra Nayak  * it under the terms of the GNU General Public License version 2 and
6*9066073cSRajendra Nayak  * only version 2 as published by the Free Software Foundation.
7*9066073cSRajendra Nayak  *
8*9066073cSRajendra Nayak  * This program is distributed in the hope that it will be useful,
9*9066073cSRajendra Nayak  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10*9066073cSRajendra Nayak  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11*9066073cSRajendra Nayak  * GNU General Public License for more details.
12*9066073cSRajendra Nayak  *
13*9066073cSRajendra Nayak  */
14*9066073cSRajendra Nayak 
15*9066073cSRajendra Nayak #include <linux/err.h>
16*9066073cSRajendra Nayak #include <linux/module.h>
17*9066073cSRajendra Nayak #include <linux/of.h>
18*9066073cSRajendra Nayak #include <linux/platform_device.h>
19*9066073cSRajendra Nayak #include <linux/pm.h>
20*9066073cSRajendra Nayak #include <linux/slab.h>
21*9066073cSRajendra Nayak #include <linux/thermal.h>
22*9066073cSRajendra Nayak #include "tsens.h"
23*9066073cSRajendra Nayak 
24*9066073cSRajendra Nayak static int tsens_get_temp(void *data, int *temp)
25*9066073cSRajendra Nayak {
26*9066073cSRajendra Nayak 	const struct tsens_sensor *s = data;
27*9066073cSRajendra Nayak 	struct tsens_device *tmdev = s->tmdev;
28*9066073cSRajendra Nayak 
29*9066073cSRajendra Nayak 	return tmdev->ops->get_temp(tmdev, s->id, temp);
30*9066073cSRajendra Nayak }
31*9066073cSRajendra Nayak 
32*9066073cSRajendra Nayak static int tsens_get_trend(void *data, long *temp)
33*9066073cSRajendra Nayak {
34*9066073cSRajendra Nayak 	const struct tsens_sensor *s = data;
35*9066073cSRajendra Nayak 	struct tsens_device *tmdev = s->tmdev;
36*9066073cSRajendra Nayak 
37*9066073cSRajendra Nayak 	if (tmdev->ops->get_trend)
38*9066073cSRajendra Nayak 		return tmdev->ops->get_trend(tmdev, s->id, temp);
39*9066073cSRajendra Nayak 
40*9066073cSRajendra Nayak 	return -ENOTSUPP;
41*9066073cSRajendra Nayak }
42*9066073cSRajendra Nayak 
43*9066073cSRajendra Nayak static int tsens_suspend(struct device *dev)
44*9066073cSRajendra Nayak {
45*9066073cSRajendra Nayak 	struct tsens_device *tmdev = dev_get_drvdata(dev);
46*9066073cSRajendra Nayak 
47*9066073cSRajendra Nayak 	if (tmdev->ops && tmdev->ops->suspend)
48*9066073cSRajendra Nayak 		return tmdev->ops->suspend(tmdev);
49*9066073cSRajendra Nayak 
50*9066073cSRajendra Nayak 	return 0;
51*9066073cSRajendra Nayak }
52*9066073cSRajendra Nayak 
53*9066073cSRajendra Nayak static int tsens_resume(struct device *dev)
54*9066073cSRajendra Nayak {
55*9066073cSRajendra Nayak 	struct tsens_device *tmdev = dev_get_drvdata(dev);
56*9066073cSRajendra Nayak 
57*9066073cSRajendra Nayak 	if (tmdev->ops && tmdev->ops->resume)
58*9066073cSRajendra Nayak 		return tmdev->ops->resume(tmdev);
59*9066073cSRajendra Nayak 
60*9066073cSRajendra Nayak 	return 0;
61*9066073cSRajendra Nayak }
62*9066073cSRajendra Nayak 
63*9066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
64*9066073cSRajendra Nayak 
65*9066073cSRajendra Nayak static const struct of_device_id tsens_table[] = {
66*9066073cSRajendra Nayak 	{
67*9066073cSRajendra Nayak 		.compatible = "qcom,msm8916-tsens",
68*9066073cSRajendra Nayak 	}, {
69*9066073cSRajendra Nayak 		.compatible = "qcom,msm8974-tsens",
70*9066073cSRajendra Nayak 	},
71*9066073cSRajendra Nayak 	{}
72*9066073cSRajendra Nayak };
73*9066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table);
74*9066073cSRajendra Nayak 
75*9066073cSRajendra Nayak static const struct thermal_zone_of_device_ops tsens_of_ops = {
76*9066073cSRajendra Nayak 	.get_temp = tsens_get_temp,
77*9066073cSRajendra Nayak 	.get_trend = tsens_get_trend,
78*9066073cSRajendra Nayak };
79*9066073cSRajendra Nayak 
80*9066073cSRajendra Nayak static int tsens_register(struct tsens_device *tmdev)
81*9066073cSRajendra Nayak {
82*9066073cSRajendra Nayak 	int i;
83*9066073cSRajendra Nayak 	struct thermal_zone_device *tzd;
84*9066073cSRajendra Nayak 	u32 *hw_id, n = tmdev->num_sensors;
85*9066073cSRajendra Nayak 
86*9066073cSRajendra Nayak 	hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
87*9066073cSRajendra Nayak 	if (!hw_id)
88*9066073cSRajendra Nayak 		return -ENOMEM;
89*9066073cSRajendra Nayak 
90*9066073cSRajendra Nayak 	for (i = 0;  i < tmdev->num_sensors; i++) {
91*9066073cSRajendra Nayak 		tmdev->sensor[i].tmdev = tmdev;
92*9066073cSRajendra Nayak 		tmdev->sensor[i].id = i;
93*9066073cSRajendra Nayak 		tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
94*9066073cSRajendra Nayak 							   &tmdev->sensor[i],
95*9066073cSRajendra Nayak 							   &tsens_of_ops);
96*9066073cSRajendra Nayak 		if (IS_ERR(tzd))
97*9066073cSRajendra Nayak 			continue;
98*9066073cSRajendra Nayak 		tmdev->sensor[i].tzd = tzd;
99*9066073cSRajendra Nayak 		if (tmdev->ops->enable)
100*9066073cSRajendra Nayak 			tmdev->ops->enable(tmdev, i);
101*9066073cSRajendra Nayak 	}
102*9066073cSRajendra Nayak 	return 0;
103*9066073cSRajendra Nayak }
104*9066073cSRajendra Nayak 
105*9066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev)
106*9066073cSRajendra Nayak {
107*9066073cSRajendra Nayak 	int ret, i;
108*9066073cSRajendra Nayak 	struct device *dev;
109*9066073cSRajendra Nayak 	struct device_node *np;
110*9066073cSRajendra Nayak 	struct tsens_sensor *s;
111*9066073cSRajendra Nayak 	struct tsens_device *tmdev;
112*9066073cSRajendra Nayak 	const struct tsens_data *data;
113*9066073cSRajendra Nayak 	const struct of_device_id *id;
114*9066073cSRajendra Nayak 
115*9066073cSRajendra Nayak 	if (pdev->dev.of_node)
116*9066073cSRajendra Nayak 		dev = &pdev->dev;
117*9066073cSRajendra Nayak 	else
118*9066073cSRajendra Nayak 		dev = pdev->dev.parent;
119*9066073cSRajendra Nayak 
120*9066073cSRajendra Nayak 	np = dev->of_node;
121*9066073cSRajendra Nayak 
122*9066073cSRajendra Nayak 	id = of_match_node(tsens_table, np);
123*9066073cSRajendra Nayak 	if (!id)
124*9066073cSRajendra Nayak 		return -EINVAL;
125*9066073cSRajendra Nayak 
126*9066073cSRajendra Nayak 	data = id->data;
127*9066073cSRajendra Nayak 
128*9066073cSRajendra Nayak 	if (data->num_sensors <= 0) {
129*9066073cSRajendra Nayak 		dev_err(dev, "invalid number of sensors\n");
130*9066073cSRajendra Nayak 		return -EINVAL;
131*9066073cSRajendra Nayak 	}
132*9066073cSRajendra Nayak 
133*9066073cSRajendra Nayak 	tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
134*9066073cSRajendra Nayak 			     data->num_sensors * sizeof(*s), GFP_KERNEL);
135*9066073cSRajendra Nayak 	if (!tmdev)
136*9066073cSRajendra Nayak 		return -ENOMEM;
137*9066073cSRajendra Nayak 
138*9066073cSRajendra Nayak 	tmdev->dev = dev;
139*9066073cSRajendra Nayak 	tmdev->num_sensors = data->num_sensors;
140*9066073cSRajendra Nayak 	tmdev->ops = data->ops;
141*9066073cSRajendra Nayak 	for (i = 0;  i < tmdev->num_sensors; i++) {
142*9066073cSRajendra Nayak 		if (data->hw_ids)
143*9066073cSRajendra Nayak 			tmdev->sensor[i].hw_id = data->hw_ids[i];
144*9066073cSRajendra Nayak 		else
145*9066073cSRajendra Nayak 			tmdev->sensor[i].hw_id = i;
146*9066073cSRajendra Nayak 	}
147*9066073cSRajendra Nayak 
148*9066073cSRajendra Nayak 	if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
149*9066073cSRajendra Nayak 		return -EINVAL;
150*9066073cSRajendra Nayak 
151*9066073cSRajendra Nayak 	ret = tmdev->ops->init(tmdev);
152*9066073cSRajendra Nayak 	if (ret < 0) {
153*9066073cSRajendra Nayak 		dev_err(dev, "tsens init failed\n");
154*9066073cSRajendra Nayak 		return ret;
155*9066073cSRajendra Nayak 	}
156*9066073cSRajendra Nayak 
157*9066073cSRajendra Nayak 	if (tmdev->ops->calibrate) {
158*9066073cSRajendra Nayak 		ret = tmdev->ops->calibrate(tmdev);
159*9066073cSRajendra Nayak 		if (ret < 0) {
160*9066073cSRajendra Nayak 			dev_err(dev, "tsens calibration failed\n");
161*9066073cSRajendra Nayak 			return ret;
162*9066073cSRajendra Nayak 		}
163*9066073cSRajendra Nayak 	}
164*9066073cSRajendra Nayak 
165*9066073cSRajendra Nayak 	ret = tsens_register(tmdev);
166*9066073cSRajendra Nayak 
167*9066073cSRajendra Nayak 	platform_set_drvdata(pdev, tmdev);
168*9066073cSRajendra Nayak 
169*9066073cSRajendra Nayak 	return ret;
170*9066073cSRajendra Nayak }
171*9066073cSRajendra Nayak 
172*9066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev)
173*9066073cSRajendra Nayak {
174*9066073cSRajendra Nayak 	struct tsens_device *tmdev = platform_get_drvdata(pdev);
175*9066073cSRajendra Nayak 
176*9066073cSRajendra Nayak 	if (tmdev->ops->disable)
177*9066073cSRajendra Nayak 		tmdev->ops->disable(tmdev);
178*9066073cSRajendra Nayak 
179*9066073cSRajendra Nayak 	return 0;
180*9066073cSRajendra Nayak }
181*9066073cSRajendra Nayak 
182*9066073cSRajendra Nayak static struct platform_driver tsens_driver = {
183*9066073cSRajendra Nayak 	.probe = tsens_probe,
184*9066073cSRajendra Nayak 	.remove = tsens_remove,
185*9066073cSRajendra Nayak 	.driver = {
186*9066073cSRajendra Nayak 		.name = "qcom-tsens",
187*9066073cSRajendra Nayak 		.pm	= &tsens_pm_ops,
188*9066073cSRajendra Nayak 		.of_match_table = tsens_table,
189*9066073cSRajendra Nayak 	},
190*9066073cSRajendra Nayak };
191*9066073cSRajendra Nayak module_platform_driver(tsens_driver);
192*9066073cSRajendra Nayak 
193*9066073cSRajendra Nayak MODULE_LICENSE("GPL v2");
194*9066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
195*9066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens");
196