Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[cris-mirror.git] / drivers / thermal / da9062-thermal.c
blobdd8dd947b7f0737c8a1228c1a7ce89ea915e44d1
1 /*
2 * Thermal device driver for DA9062 and DA9061
3 * Copyright (C) 2017 Dialog Semiconductor
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 /* When over-temperature is reached, an interrupt from the device will be
17 * triggered. Following this event the interrupt will be disabled and
18 * periodic transmission of uevents (HOT trip point) should define the
19 * first level of temperature supervision. It is expected that any final
20 * implementation of the thermal driver will include a .notify() function
21 * to implement these uevents to userspace.
23 * These uevents are intended to indicate non-invasive temperature control
24 * of the system, where the necessary measures for cooling are the
25 * responsibility of the host software. Once the temperature falls again,
26 * the IRQ is re-enabled so the start of a new over-temperature event can
27 * be detected without constant software monitoring.
30 #include <linux/errno.h>
31 #include <linux/interrupt.h>
32 #include <linux/module.h>
33 #include <linux/of.h>
34 #include <linux/platform_device.h>
35 #include <linux/regmap.h>
36 #include <linux/thermal.h>
37 #include <linux/workqueue.h>
39 #include <linux/mfd/da9062/core.h>
40 #include <linux/mfd/da9062/registers.h>
42 /* Minimum, maximum and default polling millisecond periods are provided
43 * here as an example. It is expected that any final implementation to also
44 * include a modification of these settings to match the required
45 * application.
47 #define DA9062_DEFAULT_POLLING_MS_PERIOD 3000
48 #define DA9062_MAX_POLLING_MS_PERIOD 10000
49 #define DA9062_MIN_POLLING_MS_PERIOD 1000
51 #define DA9062_MILLI_CELSIUS(t) ((t) * 1000)
53 struct da9062_thermal_config {
54 const char *name;
57 struct da9062_thermal {
58 struct da9062 *hw;
59 struct delayed_work work;
60 struct thermal_zone_device *zone;
61 enum thermal_device_mode mode;
62 struct mutex lock; /* protection for da9062_thermal temperature */
63 int temperature;
64 int irq;
65 const struct da9062_thermal_config *config;
66 struct device *dev;
69 static void da9062_thermal_poll_on(struct work_struct *work)
71 struct da9062_thermal *thermal = container_of(work,
72 struct da9062_thermal,
73 work.work);
74 unsigned long delay;
75 unsigned int val;
76 int ret;
78 /* clear E_TEMP */
79 ret = regmap_write(thermal->hw->regmap,
80 DA9062AA_EVENT_B,
81 DA9062AA_E_TEMP_MASK);
82 if (ret < 0) {
83 dev_err(thermal->dev,
84 "Cannot clear the TJUNC temperature status\n");
85 goto err_enable_irq;
88 /* Now read E_TEMP again: it is acting like a status bit.
89 * If over-temperature, then this status will be true.
90 * If not over-temperature, this status will be false.
92 ret = regmap_read(thermal->hw->regmap,
93 DA9062AA_EVENT_B,
94 &val);
95 if (ret < 0) {
96 dev_err(thermal->dev,
97 "Cannot check the TJUNC temperature status\n");
98 goto err_enable_irq;
101 if (val & DA9062AA_E_TEMP_MASK) {
102 mutex_lock(&thermal->lock);
103 thermal->temperature = DA9062_MILLI_CELSIUS(125);
104 mutex_unlock(&thermal->lock);
105 thermal_zone_device_update(thermal->zone,
106 THERMAL_EVENT_UNSPECIFIED);
108 delay = msecs_to_jiffies(thermal->zone->passive_delay);
109 schedule_delayed_work(&thermal->work, delay);
110 return;
113 mutex_lock(&thermal->lock);
114 thermal->temperature = DA9062_MILLI_CELSIUS(0);
115 mutex_unlock(&thermal->lock);
116 thermal_zone_device_update(thermal->zone,
117 THERMAL_EVENT_UNSPECIFIED);
119 err_enable_irq:
120 enable_irq(thermal->irq);
123 static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
125 struct da9062_thermal *thermal = data;
127 disable_irq_nosync(thermal->irq);
128 schedule_delayed_work(&thermal->work, 0);
130 return IRQ_HANDLED;
133 static int da9062_thermal_get_mode(struct thermal_zone_device *z,
134 enum thermal_device_mode *mode)
136 struct da9062_thermal *thermal = z->devdata;
137 *mode = thermal->mode;
138 return 0;
141 static int da9062_thermal_get_trip_type(struct thermal_zone_device *z,
142 int trip,
143 enum thermal_trip_type *type)
145 struct da9062_thermal *thermal = z->devdata;
147 switch (trip) {
148 case 0:
149 *type = THERMAL_TRIP_HOT;
150 break;
151 default:
152 dev_err(thermal->dev,
153 "Driver does not support more than 1 trip-wire\n");
154 return -EINVAL;
157 return 0;
160 static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z,
161 int trip,
162 int *temp)
164 struct da9062_thermal *thermal = z->devdata;
166 switch (trip) {
167 case 0:
168 *temp = DA9062_MILLI_CELSIUS(125);
169 break;
170 default:
171 dev_err(thermal->dev,
172 "Driver does not support more than 1 trip-wire\n");
173 return -EINVAL;
176 return 0;
179 static int da9062_thermal_get_temp(struct thermal_zone_device *z,
180 int *temp)
182 struct da9062_thermal *thermal = z->devdata;
184 mutex_lock(&thermal->lock);
185 *temp = thermal->temperature;
186 mutex_unlock(&thermal->lock);
188 return 0;
191 static struct thermal_zone_device_ops da9062_thermal_ops = {
192 .get_temp = da9062_thermal_get_temp,
193 .get_mode = da9062_thermal_get_mode,
194 .get_trip_type = da9062_thermal_get_trip_type,
195 .get_trip_temp = da9062_thermal_get_trip_temp,
198 static const struct da9062_thermal_config da9062_config = {
199 .name = "da9062-thermal",
202 static const struct of_device_id da9062_compatible_reg_id_table[] = {
203 { .compatible = "dlg,da9062-thermal", .data = &da9062_config },
204 { },
207 MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table);
209 static int da9062_thermal_probe(struct platform_device *pdev)
211 struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
212 struct da9062_thermal *thermal;
213 unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
214 const struct of_device_id *match;
215 int ret = 0;
217 match = of_match_node(da9062_compatible_reg_id_table,
218 pdev->dev.of_node);
219 if (!match)
220 return -ENXIO;
222 if (pdev->dev.of_node) {
223 if (!of_property_read_u32(pdev->dev.of_node,
224 "polling-delay-passive",
225 &pp_tmp)) {
226 if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD ||
227 pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) {
228 dev_warn(&pdev->dev,
229 "Out-of-range polling period %d ms\n",
230 pp_tmp);
231 pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
236 thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal),
237 GFP_KERNEL);
238 if (!thermal) {
239 ret = -ENOMEM;
240 goto err;
243 thermal->config = match->data;
244 thermal->hw = chip;
245 thermal->mode = THERMAL_DEVICE_ENABLED;
246 thermal->dev = &pdev->dev;
248 INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
249 mutex_init(&thermal->lock);
251 thermal->zone = thermal_zone_device_register(thermal->config->name,
252 1, 0, thermal,
253 &da9062_thermal_ops, NULL, pp_tmp,
255 if (IS_ERR(thermal->zone)) {
256 dev_err(&pdev->dev, "Cannot register thermal zone device\n");
257 ret = PTR_ERR(thermal->zone);
258 goto err;
261 dev_dbg(&pdev->dev,
262 "TJUNC temperature polling period set at %d ms\n",
263 thermal->zone->passive_delay);
265 ret = platform_get_irq_byname(pdev, "THERMAL");
266 if (ret < 0) {
267 dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
268 goto err_zone;
270 thermal->irq = ret;
272 ret = request_threaded_irq(thermal->irq, NULL,
273 da9062_thermal_irq_handler,
274 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
275 "THERMAL", thermal);
276 if (ret) {
277 dev_err(&pdev->dev,
278 "Failed to request thermal device IRQ.\n");
279 goto err_zone;
282 platform_set_drvdata(pdev, thermal);
283 return 0;
285 err_zone:
286 thermal_zone_device_unregister(thermal->zone);
287 err:
288 return ret;
291 static int da9062_thermal_remove(struct platform_device *pdev)
293 struct da9062_thermal *thermal = platform_get_drvdata(pdev);
295 free_irq(thermal->irq, thermal);
296 cancel_delayed_work_sync(&thermal->work);
297 thermal_zone_device_unregister(thermal->zone);
298 return 0;
301 static struct platform_driver da9062_thermal_driver = {
302 .probe = da9062_thermal_probe,
303 .remove = da9062_thermal_remove,
304 .driver = {
305 .name = "da9062-thermal",
306 .of_match_table = da9062_compatible_reg_id_table,
310 module_platform_driver(da9062_thermal_driver);
312 MODULE_AUTHOR("Steve Twiss");
313 MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061");
314 MODULE_LICENSE("GPL");
315 MODULE_ALIAS("platform:da9062-thermal");