xref: /linux/drivers/thermal/qcom/tsens.c (revision 0e580290170dfb438d911c306b27d89d5b99c1d9)
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 	}, {
66*0e580290SAngeloGioacchino Del Regno 		.compatible = "qcom,msm8976-tsens",
67*0e580290SAngeloGioacchino Del Regno 		.data = &data_8976,
68*0e580290SAngeloGioacchino 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 
8869b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv)
899066073cSRajendra Nayak {
90634e11d5SAmit Kucheria 	int i, ret, irq;
919066073cSRajendra Nayak 	struct thermal_zone_device *tzd;
92634e11d5SAmit Kucheria 	struct platform_device *pdev;
939066073cSRajendra Nayak 
9469b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
9569b628acSAmit Kucheria 		priv->sensor[i].priv = priv;
968b71bce4SAmit Kucheria 		tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id,
9769b628acSAmit Kucheria 							   &priv->sensor[i],
989066073cSRajendra Nayak 							   &tsens_of_ops);
999066073cSRajendra Nayak 		if (IS_ERR(tzd))
1009066073cSRajendra Nayak 			continue;
10169b628acSAmit Kucheria 		priv->sensor[i].tzd = tzd;
10269b628acSAmit Kucheria 		if (priv->ops->enable)
10369b628acSAmit Kucheria 			priv->ops->enable(priv, i);
1049066073cSRajendra Nayak 	}
105634e11d5SAmit Kucheria 
106634e11d5SAmit Kucheria 	pdev = of_find_device_by_node(priv->dev->of_node);
107634e11d5SAmit Kucheria 	if (!pdev)
108634e11d5SAmit Kucheria 		return -ENODEV;
109634e11d5SAmit Kucheria 
110634e11d5SAmit Kucheria 	irq = platform_get_irq_byname(pdev, "uplow");
111634e11d5SAmit Kucheria 	if (irq < 0) {
112634e11d5SAmit Kucheria 		ret = irq;
113634e11d5SAmit Kucheria 		goto err_put_device;
114634e11d5SAmit Kucheria 	}
115634e11d5SAmit Kucheria 
116634e11d5SAmit Kucheria 	ret = devm_request_threaded_irq(&pdev->dev, irq,
117634e11d5SAmit Kucheria 					NULL, tsens_irq_thread,
118634e11d5SAmit Kucheria 					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
119634e11d5SAmit Kucheria 					dev_name(&pdev->dev), priv);
120634e11d5SAmit Kucheria 	if (ret) {
121634e11d5SAmit Kucheria 		dev_err(&pdev->dev, "%s: failed to get irq\n", __func__);
122634e11d5SAmit Kucheria 		goto err_put_device;
123634e11d5SAmit Kucheria 	}
124634e11d5SAmit Kucheria 
125634e11d5SAmit Kucheria 	enable_irq_wake(irq);
126634e11d5SAmit Kucheria 
127634e11d5SAmit Kucheria err_put_device:
128634e11d5SAmit Kucheria 	put_device(&pdev->dev);
129634e11d5SAmit Kucheria 	return ret;
1309066073cSRajendra Nayak }
1319066073cSRajendra Nayak 
1329066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev)
1339066073cSRajendra Nayak {
1349066073cSRajendra Nayak 	int ret, i;
1359066073cSRajendra Nayak 	struct device *dev;
1369066073cSRajendra Nayak 	struct device_node *np;
13769b628acSAmit Kucheria 	struct tsens_priv *priv;
1383c040ce0SAmit Kucheria 	const struct tsens_plat_data *data;
1399066073cSRajendra Nayak 	const struct of_device_id *id;
1406d7c70d1SBjorn Andersson 	u32 num_sensors;
1419066073cSRajendra Nayak 
1429066073cSRajendra Nayak 	if (pdev->dev.of_node)
1439066073cSRajendra Nayak 		dev = &pdev->dev;
1449066073cSRajendra Nayak 	else
1459066073cSRajendra Nayak 		dev = pdev->dev.parent;
1469066073cSRajendra Nayak 
1479066073cSRajendra Nayak 	np = dev->of_node;
1489066073cSRajendra Nayak 
1499066073cSRajendra Nayak 	id = of_match_node(tsens_table, np);
15020d4fd84SRajendra Nayak 	if (id)
1519066073cSRajendra Nayak 		data = id->data;
15220d4fd84SRajendra Nayak 	else
15320d4fd84SRajendra Nayak 		data = &data_8960;
1549066073cSRajendra Nayak 
1556d7c70d1SBjorn Andersson 	num_sensors = data->num_sensors;
1566d7c70d1SBjorn Andersson 
1576d7c70d1SBjorn Andersson 	if (np)
1586d7c70d1SBjorn Andersson 		of_property_read_u32(np, "#qcom,sensors", &num_sensors);
1596d7c70d1SBjorn Andersson 
1606d7c70d1SBjorn Andersson 	if (num_sensors <= 0) {
1613795ad5eSAmit Kucheria 		dev_err(dev, "%s: invalid number of sensors\n", __func__);
1629066073cSRajendra Nayak 		return -EINVAL;
1639066073cSRajendra Nayak 	}
1649066073cSRajendra Nayak 
16569b628acSAmit Kucheria 	priv = devm_kzalloc(dev,
16669b628acSAmit Kucheria 			     struct_size(priv, sensor, num_sensors),
1670ed2dd03SKees Cook 			     GFP_KERNEL);
16869b628acSAmit Kucheria 	if (!priv)
1699066073cSRajendra Nayak 		return -ENOMEM;
1709066073cSRajendra Nayak 
17169b628acSAmit Kucheria 	priv->dev = dev;
17269b628acSAmit Kucheria 	priv->num_sensors = num_sensors;
17369b628acSAmit Kucheria 	priv->ops = data->ops;
17469b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
1759066073cSRajendra Nayak 		if (data->hw_ids)
17669b628acSAmit Kucheria 			priv->sensor[i].hw_id = data->hw_ids[i];
1779066073cSRajendra Nayak 		else
17869b628acSAmit Kucheria 			priv->sensor[i].hw_id = i;
1799066073cSRajendra Nayak 	}
180c1997054SAmit Kucheria 	priv->feat = data->feat;
181c1997054SAmit Kucheria 	priv->fields = data->fields;
1829066073cSRajendra Nayak 
1830e9c0bc7SAmit Kucheria 	platform_set_drvdata(pdev, priv);
1840e9c0bc7SAmit Kucheria 
18569b628acSAmit Kucheria 	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
1869066073cSRajendra Nayak 		return -EINVAL;
1879066073cSRajendra Nayak 
18869b628acSAmit Kucheria 	ret = priv->ops->init(priv);
1899066073cSRajendra Nayak 	if (ret < 0) {
1903795ad5eSAmit Kucheria 		dev_err(dev, "%s: init failed\n", __func__);
1919066073cSRajendra Nayak 		return ret;
1929066073cSRajendra Nayak 	}
1939066073cSRajendra Nayak 
19469b628acSAmit Kucheria 	if (priv->ops->calibrate) {
19569b628acSAmit Kucheria 		ret = priv->ops->calibrate(priv);
1969066073cSRajendra Nayak 		if (ret < 0) {
197fc7d18cfSAmit Kucheria 			if (ret != -EPROBE_DEFER)
1983795ad5eSAmit Kucheria 				dev_err(dev, "%s: calibration failed\n", __func__);
1999066073cSRajendra Nayak 			return ret;
2009066073cSRajendra Nayak 		}
2019066073cSRajendra Nayak 	}
2029066073cSRajendra Nayak 
2030e9c0bc7SAmit Kucheria 	return tsens_register(priv);
2049066073cSRajendra Nayak }
2059066073cSRajendra Nayak 
2069066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev)
2079066073cSRajendra Nayak {
20869b628acSAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
2099066073cSRajendra Nayak 
2107c938f48SAmit Kucheria 	debugfs_remove_recursive(priv->debug_root);
211634e11d5SAmit Kucheria 	tsens_disable_irq(priv);
21269b628acSAmit Kucheria 	if (priv->ops->disable)
21369b628acSAmit Kucheria 		priv->ops->disable(priv);
2149066073cSRajendra Nayak 
2159066073cSRajendra Nayak 	return 0;
2169066073cSRajendra Nayak }
2179066073cSRajendra Nayak 
2189066073cSRajendra Nayak static struct platform_driver tsens_driver = {
2199066073cSRajendra Nayak 	.probe = tsens_probe,
2209066073cSRajendra Nayak 	.remove = tsens_remove,
2219066073cSRajendra Nayak 	.driver = {
2229066073cSRajendra Nayak 		.name = "qcom-tsens",
2239066073cSRajendra Nayak 		.pm	= &tsens_pm_ops,
2249066073cSRajendra Nayak 		.of_match_table = tsens_table,
2259066073cSRajendra Nayak 	},
2269066073cSRajendra Nayak };
2279066073cSRajendra Nayak module_platform_driver(tsens_driver);
2289066073cSRajendra Nayak 
2299066073cSRajendra Nayak MODULE_LICENSE("GPL v2");
2309066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
2319066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens");
232