xref: /linux/drivers/thermal/qcom/tsens.c (revision ca64d84e93762f4e587e040a44ad9f6089afc777)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
4  */
5 
6 #include <linux/debugfs.h>
7 #include <linux/err.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/of_platform.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm.h>
13 #include <linux/slab.h>
14 #include <linux/thermal.h>
15 #include "tsens.h"
16 
17 static int tsens_get_temp(void *data, int *temp)
18 {
19 	struct tsens_sensor *s = data;
20 	struct tsens_priv *priv = s->priv;
21 
22 	return priv->ops->get_temp(s, temp);
23 }
24 
25 static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
26 {
27 	struct tsens_sensor *s = data;
28 	struct tsens_priv *priv = s->priv;
29 
30 	if (priv->ops->get_trend)
31 		return priv->ops->get_trend(s, trend);
32 
33 	return -ENOTSUPP;
34 }
35 
36 static int  __maybe_unused tsens_suspend(struct device *dev)
37 {
38 	struct tsens_priv *priv = dev_get_drvdata(dev);
39 
40 	if (priv->ops && priv->ops->suspend)
41 		return priv->ops->suspend(priv);
42 
43 	return 0;
44 }
45 
46 static int __maybe_unused tsens_resume(struct device *dev)
47 {
48 	struct tsens_priv *priv = dev_get_drvdata(dev);
49 
50 	if (priv->ops && priv->ops->resume)
51 		return priv->ops->resume(priv);
52 
53 	return 0;
54 }
55 
56 static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
57 
58 static const struct of_device_id tsens_table[] = {
59 	{
60 		.compatible = "qcom,msm8916-tsens",
61 		.data = &data_8916,
62 	}, {
63 		.compatible = "qcom,msm8974-tsens",
64 		.data = &data_8974,
65 	}, {
66 		.compatible = "qcom,msm8976-tsens",
67 		.data = &data_8976,
68 	}, {
69 		.compatible = "qcom,msm8996-tsens",
70 		.data = &data_8996,
71 	}, {
72 		.compatible = "qcom,tsens-v1",
73 		.data = &data_tsens_v1,
74 	}, {
75 		.compatible = "qcom,tsens-v2",
76 		.data = &data_tsens_v2,
77 	},
78 	{}
79 };
80 MODULE_DEVICE_TABLE(of, tsens_table);
81 
82 static const struct thermal_zone_of_device_ops tsens_of_ops = {
83 	.get_temp = tsens_get_temp,
84 	.get_trend = tsens_get_trend,
85 	.set_trips = tsens_set_trips,
86 };
87 
88 static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
89 			      irq_handler_t thread_fn)
90 {
91 	struct platform_device *pdev;
92 	int ret, irq;
93 
94 	pdev = of_find_device_by_node(priv->dev->of_node);
95 	if (!pdev)
96 		return -ENODEV;
97 
98 	irq = platform_get_irq_byname(pdev, irqname);
99 	if (irq < 0) {
100 		ret = irq;
101 		/* For old DTs with no IRQ defined */
102 		if (irq == -ENXIO)
103 			ret = 0;
104 	} else {
105 		ret = devm_request_threaded_irq(&pdev->dev, irq,
106 						NULL, thread_fn,
107 						IRQF_ONESHOT,
108 						dev_name(&pdev->dev), priv);
109 		if (ret)
110 			dev_err(&pdev->dev, "%s: failed to get irq\n",
111 				__func__);
112 		else
113 			enable_irq_wake(irq);
114 	}
115 
116 	put_device(&pdev->dev);
117 	return ret;
118 }
119 
120 static int tsens_register(struct tsens_priv *priv)
121 {
122 	int i, ret;
123 	struct thermal_zone_device *tzd;
124 
125 	for (i = 0;  i < priv->num_sensors; i++) {
126 		priv->sensor[i].priv = priv;
127 		tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id,
128 							   &priv->sensor[i],
129 							   &tsens_of_ops);
130 		if (IS_ERR(tzd))
131 			continue;
132 		priv->sensor[i].tzd = tzd;
133 		if (priv->ops->enable)
134 			priv->ops->enable(priv, i);
135 	}
136 
137 	ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
138 	if (ret < 0)
139 		return ret;
140 
141 	if (priv->feat->crit_int)
142 		ret = tsens_register_irq(priv, "critical",
143 					 tsens_critical_irq_thread);
144 
145 	return ret;
146 }
147 
148 static int tsens_probe(struct platform_device *pdev)
149 {
150 	int ret, i;
151 	struct device *dev;
152 	struct device_node *np;
153 	struct tsens_priv *priv;
154 	const struct tsens_plat_data *data;
155 	const struct of_device_id *id;
156 	u32 num_sensors;
157 
158 	if (pdev->dev.of_node)
159 		dev = &pdev->dev;
160 	else
161 		dev = pdev->dev.parent;
162 
163 	np = dev->of_node;
164 
165 	id = of_match_node(tsens_table, np);
166 	if (id)
167 		data = id->data;
168 	else
169 		data = &data_8960;
170 
171 	num_sensors = data->num_sensors;
172 
173 	if (np)
174 		of_property_read_u32(np, "#qcom,sensors", &num_sensors);
175 
176 	if (num_sensors <= 0) {
177 		dev_err(dev, "%s: invalid number of sensors\n", __func__);
178 		return -EINVAL;
179 	}
180 
181 	priv = devm_kzalloc(dev,
182 			     struct_size(priv, sensor, num_sensors),
183 			     GFP_KERNEL);
184 	if (!priv)
185 		return -ENOMEM;
186 
187 	priv->dev = dev;
188 	priv->num_sensors = num_sensors;
189 	priv->ops = data->ops;
190 	for (i = 0;  i < priv->num_sensors; i++) {
191 		if (data->hw_ids)
192 			priv->sensor[i].hw_id = data->hw_ids[i];
193 		else
194 			priv->sensor[i].hw_id = i;
195 	}
196 	priv->feat = data->feat;
197 	priv->fields = data->fields;
198 
199 	platform_set_drvdata(pdev, priv);
200 
201 	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
202 		return -EINVAL;
203 
204 	ret = priv->ops->init(priv);
205 	if (ret < 0) {
206 		dev_err(dev, "%s: init failed\n", __func__);
207 		return ret;
208 	}
209 
210 	if (priv->ops->calibrate) {
211 		ret = priv->ops->calibrate(priv);
212 		if (ret < 0) {
213 			if (ret != -EPROBE_DEFER)
214 				dev_err(dev, "%s: calibration failed\n", __func__);
215 			return ret;
216 		}
217 	}
218 
219 	return tsens_register(priv);
220 }
221 
222 static int tsens_remove(struct platform_device *pdev)
223 {
224 	struct tsens_priv *priv = platform_get_drvdata(pdev);
225 
226 	debugfs_remove_recursive(priv->debug_root);
227 	tsens_disable_irq(priv);
228 	if (priv->ops->disable)
229 		priv->ops->disable(priv);
230 
231 	return 0;
232 }
233 
234 static struct platform_driver tsens_driver = {
235 	.probe = tsens_probe,
236 	.remove = tsens_remove,
237 	.driver = {
238 		.name = "qcom-tsens",
239 		.pm	= &tsens_pm_ops,
240 		.of_match_table = tsens_table,
241 	},
242 };
243 module_platform_driver(tsens_driver);
244 
245 MODULE_LICENSE("GPL v2");
246 MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
247 MODULE_ALIAS("platform:qcom-tsens");
248