xref: /linux/drivers/thermal/qcom/tsens.c (revision 79125e03dbfc7148361787783c8a82a7ed60c718)
12d71d8deSAmit Kucheria // SPDX-License-Identifier: GPL-2.0
29066073cSRajendra Nayak /*
39066073cSRajendra Nayak  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
49066073cSRajendra Nayak  */
59066073cSRajendra Nayak 
67c938f48SAmit Kucheria #include <linux/debugfs.h>
79066073cSRajendra Nayak #include <linux/err.h>
89066073cSRajendra Nayak #include <linux/module.h>
99066073cSRajendra Nayak #include <linux/of.h>
10634e11d5SAmit Kucheria #include <linux/of_platform.h>
119066073cSRajendra Nayak #include <linux/platform_device.h>
129066073cSRajendra Nayak #include <linux/pm.h>
139066073cSRajendra Nayak #include <linux/slab.h>
149066073cSRajendra Nayak #include <linux/thermal.h>
159066073cSRajendra Nayak #include "tsens.h"
169066073cSRajendra Nayak 
179066073cSRajendra Nayak static int tsens_get_temp(void *data, int *temp)
189066073cSRajendra Nayak {
198b71bce4SAmit Kucheria 	struct tsens_sensor *s = data;
2069b628acSAmit Kucheria 	struct tsens_priv *priv = s->priv;
219066073cSRajendra Nayak 
228b71bce4SAmit Kucheria 	return priv->ops->get_temp(s, temp);
239066073cSRajendra Nayak }
249066073cSRajendra Nayak 
252cbcd2eaSAmit Kucheria static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
269066073cSRajendra Nayak {
278b71bce4SAmit Kucheria 	struct tsens_sensor *s = data;
2869b628acSAmit Kucheria 	struct tsens_priv *priv = s->priv;
299066073cSRajendra Nayak 
3069b628acSAmit Kucheria 	if (priv->ops->get_trend)
318b71bce4SAmit Kucheria 		return priv->ops->get_trend(s, trend);
329066073cSRajendra Nayak 
339066073cSRajendra Nayak 	return -ENOTSUPP;
349066073cSRajendra Nayak }
359066073cSRajendra Nayak 
365b97469aSArnd Bergmann static int  __maybe_unused tsens_suspend(struct device *dev)
379066073cSRajendra Nayak {
3869b628acSAmit Kucheria 	struct tsens_priv *priv = dev_get_drvdata(dev);
399066073cSRajendra Nayak 
4069b628acSAmit Kucheria 	if (priv->ops && priv->ops->suspend)
4169b628acSAmit Kucheria 		return priv->ops->suspend(priv);
429066073cSRajendra Nayak 
439066073cSRajendra Nayak 	return 0;
449066073cSRajendra Nayak }
459066073cSRajendra Nayak 
465b97469aSArnd Bergmann static int __maybe_unused tsens_resume(struct device *dev)
479066073cSRajendra Nayak {
4869b628acSAmit Kucheria 	struct tsens_priv *priv = dev_get_drvdata(dev);
499066073cSRajendra Nayak 
5069b628acSAmit Kucheria 	if (priv->ops && priv->ops->resume)
5169b628acSAmit Kucheria 		return priv->ops->resume(priv);
529066073cSRajendra Nayak 
539066073cSRajendra Nayak 	return 0;
549066073cSRajendra Nayak }
559066073cSRajendra Nayak 
569066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
579066073cSRajendra Nayak 
589066073cSRajendra Nayak static const struct of_device_id tsens_table[] = {
599066073cSRajendra Nayak 	{
609066073cSRajendra Nayak 		.compatible = "qcom,msm8916-tsens",
61840a5bd3SRajendra Nayak 		.data = &data_8916,
629066073cSRajendra Nayak 	}, {
639066073cSRajendra Nayak 		.compatible = "qcom,msm8974-tsens",
645e6703bdSRajendra Nayak 		.data = &data_8974,
65d059c739SRajendra Nayak 	}, {
660e580290SAngeloGioacchino Del Regno 		.compatible = "qcom,msm8976-tsens",
670e580290SAngeloGioacchino Del Regno 		.data = &data_8976,
680e580290SAngeloGioacchino Del Regno 	}, {
69d059c739SRajendra Nayak 		.compatible = "qcom,msm8996-tsens",
70d059c739SRajendra Nayak 		.data = &data_8996,
71191dc74bSAmit Kucheria 	}, {
72e8c24c6fSAmit Kucheria 		.compatible = "qcom,tsens-v1",
73e8c24c6fSAmit Kucheria 		.data = &data_tsens_v1,
74e8c24c6fSAmit Kucheria 	}, {
75191dc74bSAmit Kucheria 		.compatible = "qcom,tsens-v2",
76191dc74bSAmit Kucheria 		.data = &data_tsens_v2,
779066073cSRajendra Nayak 	},
789066073cSRajendra Nayak 	{}
799066073cSRajendra Nayak };
809066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table);
819066073cSRajendra Nayak 
829066073cSRajendra Nayak static const struct thermal_zone_of_device_ops tsens_of_ops = {
839066073cSRajendra Nayak 	.get_temp = tsens_get_temp,
849066073cSRajendra Nayak 	.get_trend = tsens_get_trend,
85634e11d5SAmit Kucheria 	.set_trips = tsens_set_trips,
869066073cSRajendra Nayak };
879066073cSRajendra Nayak 
88*79125e03SAmit Kucheria static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
89*79125e03SAmit Kucheria 			      irq_handler_t thread_fn)
90*79125e03SAmit Kucheria {
91*79125e03SAmit Kucheria 	struct platform_device *pdev;
92*79125e03SAmit Kucheria 	int ret, irq;
93*79125e03SAmit Kucheria 
94*79125e03SAmit Kucheria 	pdev = of_find_device_by_node(priv->dev->of_node);
95*79125e03SAmit Kucheria 	if (!pdev)
96*79125e03SAmit Kucheria 		return -ENODEV;
97*79125e03SAmit Kucheria 
98*79125e03SAmit Kucheria 	irq = platform_get_irq_byname(pdev, irqname);
99*79125e03SAmit Kucheria 	if (irq < 0) {
100*79125e03SAmit Kucheria 		ret = irq;
101*79125e03SAmit Kucheria 		/* For old DTs with no IRQ defined */
102*79125e03SAmit Kucheria 		if (irq == -ENXIO)
103*79125e03SAmit Kucheria 			ret = 0;
104*79125e03SAmit Kucheria 	} else {
105*79125e03SAmit Kucheria 		ret = devm_request_threaded_irq(&pdev->dev, irq,
106*79125e03SAmit Kucheria 						NULL, thread_fn,
107*79125e03SAmit Kucheria 						IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
108*79125e03SAmit Kucheria 						dev_name(&pdev->dev), priv);
109*79125e03SAmit Kucheria 		if (ret)
110*79125e03SAmit Kucheria 			dev_err(&pdev->dev, "%s: failed to get irq\n",
111*79125e03SAmit Kucheria 				__func__);
112*79125e03SAmit Kucheria 		else
113*79125e03SAmit Kucheria 			enable_irq_wake(irq);
114*79125e03SAmit Kucheria 	}
115*79125e03SAmit Kucheria 
116*79125e03SAmit Kucheria 	put_device(&pdev->dev);
117*79125e03SAmit Kucheria 	return ret;
118*79125e03SAmit Kucheria }
119*79125e03SAmit Kucheria 
12069b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv)
1219066073cSRajendra Nayak {
122*79125e03SAmit Kucheria 	int i, ret;
1239066073cSRajendra Nayak 	struct thermal_zone_device *tzd;
1249066073cSRajendra Nayak 
12569b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
12669b628acSAmit Kucheria 		priv->sensor[i].priv = priv;
1278b71bce4SAmit Kucheria 		tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id,
12869b628acSAmit Kucheria 							   &priv->sensor[i],
1299066073cSRajendra Nayak 							   &tsens_of_ops);
1309066073cSRajendra Nayak 		if (IS_ERR(tzd))
1319066073cSRajendra Nayak 			continue;
13269b628acSAmit Kucheria 		priv->sensor[i].tzd = tzd;
13369b628acSAmit Kucheria 		if (priv->ops->enable)
13469b628acSAmit Kucheria 			priv->ops->enable(priv, i);
1359066073cSRajendra Nayak 	}
136634e11d5SAmit Kucheria 
137*79125e03SAmit Kucheria 	ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
138*79125e03SAmit Kucheria 	if (ret < 0)
139*79125e03SAmit Kucheria 		return ret;
140634e11d5SAmit Kucheria 
141*79125e03SAmit Kucheria 	if (priv->feat->crit_int)
142*79125e03SAmit Kucheria 		ret = tsens_register_irq(priv, "critical",
143*79125e03SAmit Kucheria 					 tsens_critical_irq_thread);
144634e11d5SAmit Kucheria 
145634e11d5SAmit Kucheria 	return ret;
1469066073cSRajendra Nayak }
1479066073cSRajendra Nayak 
1489066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev)
1499066073cSRajendra Nayak {
1509066073cSRajendra Nayak 	int ret, i;
1519066073cSRajendra Nayak 	struct device *dev;
1529066073cSRajendra Nayak 	struct device_node *np;
15369b628acSAmit Kucheria 	struct tsens_priv *priv;
1543c040ce0SAmit Kucheria 	const struct tsens_plat_data *data;
1559066073cSRajendra Nayak 	const struct of_device_id *id;
1566d7c70d1SBjorn Andersson 	u32 num_sensors;
1579066073cSRajendra Nayak 
1589066073cSRajendra Nayak 	if (pdev->dev.of_node)
1599066073cSRajendra Nayak 		dev = &pdev->dev;
1609066073cSRajendra Nayak 	else
1619066073cSRajendra Nayak 		dev = pdev->dev.parent;
1629066073cSRajendra Nayak 
1639066073cSRajendra Nayak 	np = dev->of_node;
1649066073cSRajendra Nayak 
1659066073cSRajendra Nayak 	id = of_match_node(tsens_table, np);
16620d4fd84SRajendra Nayak 	if (id)
1679066073cSRajendra Nayak 		data = id->data;
16820d4fd84SRajendra Nayak 	else
16920d4fd84SRajendra Nayak 		data = &data_8960;
1709066073cSRajendra Nayak 
1716d7c70d1SBjorn Andersson 	num_sensors = data->num_sensors;
1726d7c70d1SBjorn Andersson 
1736d7c70d1SBjorn Andersson 	if (np)
1746d7c70d1SBjorn Andersson 		of_property_read_u32(np, "#qcom,sensors", &num_sensors);
1756d7c70d1SBjorn Andersson 
1766d7c70d1SBjorn Andersson 	if (num_sensors <= 0) {
1773795ad5eSAmit Kucheria 		dev_err(dev, "%s: invalid number of sensors\n", __func__);
1789066073cSRajendra Nayak 		return -EINVAL;
1799066073cSRajendra Nayak 	}
1809066073cSRajendra Nayak 
18169b628acSAmit Kucheria 	priv = devm_kzalloc(dev,
18269b628acSAmit Kucheria 			     struct_size(priv, sensor, num_sensors),
1830ed2dd03SKees Cook 			     GFP_KERNEL);
18469b628acSAmit Kucheria 	if (!priv)
1859066073cSRajendra Nayak 		return -ENOMEM;
1869066073cSRajendra Nayak 
18769b628acSAmit Kucheria 	priv->dev = dev;
18869b628acSAmit Kucheria 	priv->num_sensors = num_sensors;
18969b628acSAmit Kucheria 	priv->ops = data->ops;
19069b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
1919066073cSRajendra Nayak 		if (data->hw_ids)
19269b628acSAmit Kucheria 			priv->sensor[i].hw_id = data->hw_ids[i];
1939066073cSRajendra Nayak 		else
19469b628acSAmit Kucheria 			priv->sensor[i].hw_id = i;
1959066073cSRajendra Nayak 	}
196c1997054SAmit Kucheria 	priv->feat = data->feat;
197c1997054SAmit Kucheria 	priv->fields = data->fields;
1989066073cSRajendra Nayak 
1990e9c0bc7SAmit Kucheria 	platform_set_drvdata(pdev, priv);
2000e9c0bc7SAmit Kucheria 
20169b628acSAmit Kucheria 	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
2029066073cSRajendra Nayak 		return -EINVAL;
2039066073cSRajendra Nayak 
20469b628acSAmit Kucheria 	ret = priv->ops->init(priv);
2059066073cSRajendra Nayak 	if (ret < 0) {
2063795ad5eSAmit Kucheria 		dev_err(dev, "%s: init failed\n", __func__);
2079066073cSRajendra Nayak 		return ret;
2089066073cSRajendra Nayak 	}
2099066073cSRajendra Nayak 
21069b628acSAmit Kucheria 	if (priv->ops->calibrate) {
21169b628acSAmit Kucheria 		ret = priv->ops->calibrate(priv);
2129066073cSRajendra Nayak 		if (ret < 0) {
213fc7d18cfSAmit Kucheria 			if (ret != -EPROBE_DEFER)
2143795ad5eSAmit Kucheria 				dev_err(dev, "%s: calibration failed\n", __func__);
2159066073cSRajendra Nayak 			return ret;
2169066073cSRajendra Nayak 		}
2179066073cSRajendra Nayak 	}
2189066073cSRajendra Nayak 
2190e9c0bc7SAmit Kucheria 	return tsens_register(priv);
2209066073cSRajendra Nayak }
2219066073cSRajendra Nayak 
2229066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev)
2239066073cSRajendra Nayak {
22469b628acSAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
2259066073cSRajendra Nayak 
2267c938f48SAmit Kucheria 	debugfs_remove_recursive(priv->debug_root);
227634e11d5SAmit Kucheria 	tsens_disable_irq(priv);
22869b628acSAmit Kucheria 	if (priv->ops->disable)
22969b628acSAmit Kucheria 		priv->ops->disable(priv);
2309066073cSRajendra Nayak 
2319066073cSRajendra Nayak 	return 0;
2329066073cSRajendra Nayak }
2339066073cSRajendra Nayak 
2349066073cSRajendra Nayak static struct platform_driver tsens_driver = {
2359066073cSRajendra Nayak 	.probe = tsens_probe,
2369066073cSRajendra Nayak 	.remove = tsens_remove,
2379066073cSRajendra Nayak 	.driver = {
2389066073cSRajendra Nayak 		.name = "qcom-tsens",
2399066073cSRajendra Nayak 		.pm	= &tsens_pm_ops,
2409066073cSRajendra Nayak 		.of_match_table = tsens_table,
2419066073cSRajendra Nayak 	},
2429066073cSRajendra Nayak };
2439066073cSRajendra Nayak module_platform_driver(tsens_driver);
2449066073cSRajendra Nayak 
2459066073cSRajendra Nayak MODULE_LICENSE("GPL v2");
2469066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
2479066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens");
248