blob: 6241ef61ff09b40f42056b21652dbcafb4cac495 [file] [log] [blame]
Rama Krishna Phani Ae677c1c2018-01-31 18:51:43 +05301/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -08002 *
3 * This program is free software; you can redistribute it and/or modify
Siddartha Mohanadosscd8aa0b2017-04-06 15:17:46 -07004 * it under the terms of the GNU General Public License version 2 and
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -08005 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/err.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/platform_device.h>
18#include <linux/pm.h>
19#include <linux/kernel.h>
20#include <linux/io.h>
21#include <linux/slab.h>
22#include <linux/thermal.h>
23#include "tsens.h"
Ram Chandrasekar86eef432017-05-15 15:21:03 -060024#include "qcom/qti_virtual_sensor.h"
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080025
26LIST_HEAD(tsens_device_list);
27
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070028static int tsens_get_temp(void *data, int *temp)
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080029{
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070030 struct tsens_sensor *s = data;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080031 struct tsens_device *tmdev = s->tmdev;
32
33 return tmdev->ops->get_temp(s, temp);
34}
35
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070036static int tsens_set_trip_temp(void *data, int low_temp, int high_temp)
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080037{
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070038 struct tsens_sensor *s = data;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080039 struct tsens_device *tmdev = s->tmdev;
40
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070041 if (tmdev->ops->set_trips)
42 return tmdev->ops->set_trips(s, low_temp, high_temp);
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080043
44 return 0;
45}
46
47static int tsens_init(struct tsens_device *tmdev)
48{
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -070049 return tmdev->ops->hw_init(tmdev);
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080050}
51
Rama Krishna Phani Ae677c1c2018-01-31 18:51:43 +053052static int tsens_calib(struct tsens_device *tmdev)
53{
54 return tmdev->ops->calibrate(tmdev);
55}
56
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080057static int tsens_register_interrupts(struct tsens_device *tmdev)
58{
59 if (tmdev->ops->interrupts_reg)
60 return tmdev->ops->interrupts_reg(tmdev);
61
62 return 0;
63}
64
65static const struct of_device_id tsens_table[] = {
66 { .compatible = "qcom,msm8996-tsens",
67 .data = &data_tsens2xxx,
68 },
69 { .compatible = "qcom,msm8953-tsens",
70 .data = &data_tsens2xxx,
71 },
72 { .compatible = "qcom,msm8998-tsens",
73 .data = &data_tsens2xxx,
74 },
75 { .compatible = "qcom,msmhamster-tsens",
76 .data = &data_tsens2xxx,
77 },
78 { .compatible = "qcom,sdm660-tsens",
79 .data = &data_tsens23xx,
80 },
81 { .compatible = "qcom,sdm630-tsens",
82 .data = &data_tsens23xx,
83 },
84 { .compatible = "qcom,sdm845-tsens",
85 .data = &data_tsens24xx,
86 },
Siddartha Mohanadossc96805e2017-07-18 12:42:57 -070087 { .compatible = "qcom,tsens24xx",
88 .data = &data_tsens24xx,
89 },
Rama Krishna Phani Ae677c1c2018-01-31 18:51:43 +053090 { .compatible = "qcom,msm8937-tsens",
91 .data = &data_tsens14xx,
92 },
Chinkit Kumar,Kirti Kumar Parmar253de882018-06-06 00:11:56 +053093 { .compatible = "qcom,msm8909-tsens",
94 .data = &data_tsens1xxx_8909,
95 },
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -080096 {}
97};
98MODULE_DEVICE_TABLE(of, tsens_table);
99
100static struct thermal_zone_of_device_ops tsens_tm_thermal_zone_ops = {
101 .get_temp = tsens_get_temp,
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700102 .set_trips = tsens_set_trip_temp,
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800103};
104
105static int get_device_tree_data(struct platform_device *pdev,
106 struct tsens_device *tmdev)
107{
108 struct device_node *of_node = pdev->dev.of_node;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800109 const struct of_device_id *id;
110 const struct tsens_data *data;
Rama Krishna Phani Ae677c1c2018-01-31 18:51:43 +0530111 int rc = 0;
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700112 struct resource *res_tsens_mem;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800113
114 if (!of_match_node(tsens_table, of_node)) {
115 pr_err("Need to read SoC specific fuse map\n");
116 return -ENODEV;
117 }
118
119 id = of_match_node(tsens_table, of_node);
120 if (id == NULL) {
121 pr_err("can not find tsens_table of_node\n");
122 return -ENODEV;
123 }
124
125 data = id->data;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800126 tmdev->ops = data->ops;
127 tmdev->ctrl_data = data;
128 tmdev->pdev = pdev;
129
130 if (!tmdev->ops || !tmdev->ops->hw_init || !tmdev->ops->get_temp) {
131 pr_err("Invalid ops\n");
132 return -EINVAL;
133 }
134
135 /* TSENS register region */
136 res_tsens_mem = platform_get_resource_byname(pdev,
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700137 IORESOURCE_MEM, "tsens_srot_physical");
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800138 if (!res_tsens_mem) {
139 pr_err("Could not get tsens physical address resource\n");
140 return -EINVAL;
141 }
142
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700143 tmdev->tsens_srot_addr = devm_ioremap_resource(&pdev->dev,
144 res_tsens_mem);
145 if (IS_ERR(tmdev->tsens_srot_addr)) {
146 dev_err(&pdev->dev, "Failed to IO map TSENS registers.\n");
147 return PTR_ERR(tmdev->tsens_srot_addr);
148 }
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800149
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700150 /* TSENS TM register region */
151 res_tsens_mem = platform_get_resource_byname(pdev,
152 IORESOURCE_MEM, "tsens_tm_physical");
153 if (!res_tsens_mem) {
154 pr_err("Could not get tsens physical address resource\n");
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800155 return -EINVAL;
156 }
157
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700158 tmdev->tsens_tm_addr = devm_ioremap_resource(&pdev->dev,
159 res_tsens_mem);
160 if (IS_ERR(tmdev->tsens_tm_addr)) {
161 dev_err(&pdev->dev, "Failed to IO map TSENS TM registers.\n");
162 return PTR_ERR(tmdev->tsens_tm_addr);
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800163 }
164
Rama Krishna Phani Ae677c1c2018-01-31 18:51:43 +0530165 /* TSENS eeprom register region */
166 res_tsens_mem = platform_get_resource_byname(pdev,
167 IORESOURCE_MEM, "tsens_eeprom_physical");
168 if (!res_tsens_mem) {
169 pr_debug("Could not get tsens physical address resource\n");
170 } else {
171 tmdev->tsens_calib_addr = devm_ioremap_resource(&pdev->dev,
172 res_tsens_mem);
173 if (IS_ERR(tmdev->tsens_calib_addr)) {
174 dev_err(&pdev->dev, "Failed to IO map TSENS EEPROM registers.\n");
175 rc = PTR_ERR(tmdev->tsens_calib_addr);
176 } else {
177 rc = tsens_calib(tmdev);
178 if (rc) {
179 pr_err("Error initializing TSENS controller\n");
180 return rc;
181 }
182 }
183 }
184
185 return rc;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800186}
187
188static int tsens_thermal_zone_register(struct tsens_device *tmdev)
189{
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700190 int i = 0, sensor_missing = 0;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800191
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700192 for (i = 0; i < TSENS_MAX_SENSORS; i++) {
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800193 tmdev->sensor[i].tmdev = tmdev;
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700194 tmdev->sensor[i].hw_id = i;
Siddartha Mohanadossf86413d2017-07-18 13:44:31 -0700195 if (tmdev->ops->sensor_en(tmdev, i)) {
196 tmdev->sensor[i].tzd =
197 devm_thermal_zone_of_sensor_register(
198 &tmdev->pdev->dev, i,
199 &tmdev->sensor[i], &tsens_tm_thermal_zone_ops);
200 if (IS_ERR(tmdev->sensor[i].tzd)) {
201 pr_debug("Error registering sensor:%d\n", i);
202 sensor_missing++;
203 continue;
204 }
205 } else {
206 pr_debug("Sensor not enabled:%d\n", i);
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800207 }
208 }
209
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700210 if (sensor_missing == TSENS_MAX_SENSORS) {
211 pr_err("No TSENS sensors to register?\n");
212 return -ENODEV;
213 }
214
Ram Chandrasekar86eef432017-05-15 15:21:03 -0600215 /* Register virtual thermal sensors. */
216 qti_virtual_sensor_register(&tmdev->pdev->dev);
217
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700218 return 0;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800219}
220
221static int tsens_tm_remove(struct platform_device *pdev)
222{
223 platform_set_drvdata(pdev, NULL);
224
225 return 0;
226}
227
228int tsens_tm_probe(struct platform_device *pdev)
229{
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800230 struct tsens_device *tmdev = NULL;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800231 int rc;
232
233 if (!(pdev->dev.of_node))
234 return -ENODEV;
235
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800236 tmdev = devm_kzalloc(&pdev->dev,
237 sizeof(struct tsens_device) +
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700238 TSENS_MAX_SENSORS *
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800239 sizeof(struct tsens_sensor),
240 GFP_KERNEL);
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700241 if (tmdev == NULL)
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800242 return -ENOMEM;
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800243
244 rc = get_device_tree_data(pdev, tmdev);
245 if (rc) {
246 pr_err("Error reading TSENS DT\n");
247 return rc;
248 }
249
250 rc = tsens_init(tmdev);
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700251 if (rc) {
252 pr_err("Error initializing TSENS controller\n");
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800253 return rc;
Siddartha Mohanadoss07b10612017-04-07 14:53:45 -0700254 }
Siddartha Mohanadoss41b4cd92017-02-21 14:34:23 -0800255
256 rc = tsens_thermal_zone_register(tmdev);
257 if (rc) {
258 pr_err("Error registering the thermal zone\n");
259 return rc;
260 }
261
262 rc = tsens_register_interrupts(tmdev);
263 if (rc < 0) {
264 pr_err("TSENS interrupt register failed:%d\n", rc);
265 return rc;
266 }
267
268 list_add_tail(&tmdev->list, &tsens_device_list);
269 platform_set_drvdata(pdev, tmdev);
270
271 return rc;
272}
273
274static struct platform_driver tsens_tm_driver = {
275 .probe = tsens_tm_probe,
276 .remove = tsens_tm_remove,
277 .driver = {
278 .name = "msm-tsens",
279 .owner = THIS_MODULE,
280 .of_match_table = tsens_table,
281 },
282};
283
284int __init tsens_tm_init_driver(void)
285{
286 return platform_driver_register(&tsens_tm_driver);
287}
288subsys_initcall(tsens_tm_init_driver);
289
290static void __exit tsens_tm_deinit(void)
291{
292 platform_driver_unregister(&tsens_tm_driver);
293}
294module_exit(tsens_tm_deinit);
295
296MODULE_ALIAS("platform:" TSENS_DRIVER_NAME);
297MODULE_LICENSE("GPL v2");