1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2608567aaSSteve Twiss /*
3608567aaSSteve Twiss * Thermal device driver for DA9062 and DA9061
4608567aaSSteve Twiss * Copyright (C) 2017 Dialog Semiconductor
5608567aaSSteve Twiss */
6608567aaSSteve Twiss
7608567aaSSteve Twiss /* When over-temperature is reached, an interrupt from the device will be
8608567aaSSteve Twiss * triggered. Following this event the interrupt will be disabled and
9608567aaSSteve Twiss * periodic transmission of uevents (HOT trip point) should define the
10608567aaSSteve Twiss * first level of temperature supervision. It is expected that any final
11608567aaSSteve Twiss * implementation of the thermal driver will include a .notify() function
12608567aaSSteve Twiss * to implement these uevents to userspace.
13608567aaSSteve Twiss *
14608567aaSSteve Twiss * These uevents are intended to indicate non-invasive temperature control
15608567aaSSteve Twiss * of the system, where the necessary measures for cooling are the
16608567aaSSteve Twiss * responsibility of the host software. Once the temperature falls again,
17608567aaSSteve Twiss * the IRQ is re-enabled so the start of a new over-temperature event can
18608567aaSSteve Twiss * be detected without constant software monitoring.
19608567aaSSteve Twiss */
20608567aaSSteve Twiss
21608567aaSSteve Twiss #include <linux/errno.h>
22608567aaSSteve Twiss #include <linux/interrupt.h>
23608567aaSSteve Twiss #include <linux/module.h>
24608567aaSSteve Twiss #include <linux/of.h>
25608567aaSSteve Twiss #include <linux/platform_device.h>
26608567aaSSteve Twiss #include <linux/regmap.h>
27608567aaSSteve Twiss #include <linux/thermal.h>
28608567aaSSteve Twiss #include <linux/workqueue.h>
29608567aaSSteve Twiss
30608567aaSSteve Twiss #include <linux/mfd/da9062/core.h>
31608567aaSSteve Twiss #include <linux/mfd/da9062/registers.h>
32608567aaSSteve Twiss
33608567aaSSteve Twiss /* Minimum, maximum and default polling millisecond periods are provided
34608567aaSSteve Twiss * here as an example. It is expected that any final implementation to also
35608567aaSSteve Twiss * include a modification of these settings to match the required
36608567aaSSteve Twiss * application.
37608567aaSSteve Twiss */
38608567aaSSteve Twiss #define DA9062_DEFAULT_POLLING_MS_PERIOD 3000
39608567aaSSteve Twiss #define DA9062_MAX_POLLING_MS_PERIOD 10000
40608567aaSSteve Twiss #define DA9062_MIN_POLLING_MS_PERIOD 1000
41608567aaSSteve Twiss
42608567aaSSteve Twiss #define DA9062_MILLI_CELSIUS(t) ((t) * 1000)
43608567aaSSteve Twiss
440adad092SDaniel Lezcano static unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
450adad092SDaniel Lezcano
46608567aaSSteve Twiss struct da9062_thermal_config {
47608567aaSSteve Twiss const char *name;
48608567aaSSteve Twiss };
49608567aaSSteve Twiss
50608567aaSSteve Twiss struct da9062_thermal {
51608567aaSSteve Twiss struct da9062 *hw;
52608567aaSSteve Twiss struct delayed_work work;
53608567aaSSteve Twiss struct thermal_zone_device *zone;
54608567aaSSteve Twiss struct mutex lock; /* protection for da9062_thermal temperature */
55608567aaSSteve Twiss int temperature;
56608567aaSSteve Twiss int irq;
57608567aaSSteve Twiss const struct da9062_thermal_config *config;
58608567aaSSteve Twiss struct device *dev;
59608567aaSSteve Twiss };
60608567aaSSteve Twiss
da9062_thermal_poll_on(struct work_struct * work)61608567aaSSteve Twiss static void da9062_thermal_poll_on(struct work_struct *work)
62608567aaSSteve Twiss {
63608567aaSSteve Twiss struct da9062_thermal *thermal = container_of(work,
64608567aaSSteve Twiss struct da9062_thermal,
65608567aaSSteve Twiss work.work);
66608567aaSSteve Twiss unsigned long delay;
67608567aaSSteve Twiss unsigned int val;
68608567aaSSteve Twiss int ret;
69608567aaSSteve Twiss
70608567aaSSteve Twiss /* clear E_TEMP */
71608567aaSSteve Twiss ret = regmap_write(thermal->hw->regmap,
72608567aaSSteve Twiss DA9062AA_EVENT_B,
73608567aaSSteve Twiss DA9062AA_E_TEMP_MASK);
74608567aaSSteve Twiss if (ret < 0) {
75608567aaSSteve Twiss dev_err(thermal->dev,
76608567aaSSteve Twiss "Cannot clear the TJUNC temperature status\n");
77608567aaSSteve Twiss goto err_enable_irq;
78608567aaSSteve Twiss }
79608567aaSSteve Twiss
80608567aaSSteve Twiss /* Now read E_TEMP again: it is acting like a status bit.
81608567aaSSteve Twiss * If over-temperature, then this status will be true.
82608567aaSSteve Twiss * If not over-temperature, this status will be false.
83608567aaSSteve Twiss */
84608567aaSSteve Twiss ret = regmap_read(thermal->hw->regmap,
85608567aaSSteve Twiss DA9062AA_EVENT_B,
86608567aaSSteve Twiss &val);
87608567aaSSteve Twiss if (ret < 0) {
88608567aaSSteve Twiss dev_err(thermal->dev,
89608567aaSSteve Twiss "Cannot check the TJUNC temperature status\n");
90608567aaSSteve Twiss goto err_enable_irq;
91608567aaSSteve Twiss }
92608567aaSSteve Twiss
93608567aaSSteve Twiss if (val & DA9062AA_E_TEMP_MASK) {
94608567aaSSteve Twiss mutex_lock(&thermal->lock);
95608567aaSSteve Twiss thermal->temperature = DA9062_MILLI_CELSIUS(125);
96608567aaSSteve Twiss mutex_unlock(&thermal->lock);
97608567aaSSteve Twiss thermal_zone_device_update(thermal->zone,
98608567aaSSteve Twiss THERMAL_EVENT_UNSPECIFIED);
99608567aaSSteve Twiss
1000adad092SDaniel Lezcano /*
1010adad092SDaniel Lezcano * pp_tmp is between 1s and 10s, so we can round the jiffies
1020adad092SDaniel Lezcano */
1030adad092SDaniel Lezcano delay = round_jiffies(msecs_to_jiffies(pp_tmp));
104760eea43SGeert Uytterhoeven queue_delayed_work(system_freezable_wq, &thermal->work, delay);
105608567aaSSteve Twiss return;
106608567aaSSteve Twiss }
107608567aaSSteve Twiss
108608567aaSSteve Twiss mutex_lock(&thermal->lock);
109608567aaSSteve Twiss thermal->temperature = DA9062_MILLI_CELSIUS(0);
110608567aaSSteve Twiss mutex_unlock(&thermal->lock);
111608567aaSSteve Twiss thermal_zone_device_update(thermal->zone,
112608567aaSSteve Twiss THERMAL_EVENT_UNSPECIFIED);
113608567aaSSteve Twiss
114608567aaSSteve Twiss err_enable_irq:
115608567aaSSteve Twiss enable_irq(thermal->irq);
116608567aaSSteve Twiss }
117608567aaSSteve Twiss
da9062_thermal_irq_handler(int irq,void * data)118608567aaSSteve Twiss static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
119608567aaSSteve Twiss {
120608567aaSSteve Twiss struct da9062_thermal *thermal = data;
121608567aaSSteve Twiss
122608567aaSSteve Twiss disable_irq_nosync(thermal->irq);
123760eea43SGeert Uytterhoeven queue_delayed_work(system_freezable_wq, &thermal->work, 0);
124608567aaSSteve Twiss
125608567aaSSteve Twiss return IRQ_HANDLED;
126608567aaSSteve Twiss }
127608567aaSSteve Twiss
da9062_thermal_get_temp(struct thermal_zone_device * z,int * temp)128608567aaSSteve Twiss static int da9062_thermal_get_temp(struct thermal_zone_device *z,
129608567aaSSteve Twiss int *temp)
130608567aaSSteve Twiss {
1315f68d078SDaniel Lezcano struct da9062_thermal *thermal = thermal_zone_device_priv(z);
132608567aaSSteve Twiss
133608567aaSSteve Twiss mutex_lock(&thermal->lock);
134608567aaSSteve Twiss *temp = thermal->temperature;
135608567aaSSteve Twiss mutex_unlock(&thermal->lock);
136608567aaSSteve Twiss
137608567aaSSteve Twiss return 0;
138608567aaSSteve Twiss }
139608567aaSSteve Twiss
140608567aaSSteve Twiss static struct thermal_zone_device_ops da9062_thermal_ops = {
141608567aaSSteve Twiss .get_temp = da9062_thermal_get_temp,
142060b39d9SDaniel Lezcano };
143060b39d9SDaniel Lezcano
144060b39d9SDaniel Lezcano static struct thermal_trip trips[] = {
145060b39d9SDaniel Lezcano { .temperature = DA9062_MILLI_CELSIUS(125), .type = THERMAL_TRIP_HOT },
146608567aaSSteve Twiss };
147608567aaSSteve Twiss
148608567aaSSteve Twiss static const struct da9062_thermal_config da9062_config = {
149608567aaSSteve Twiss .name = "da9062-thermal",
150608567aaSSteve Twiss };
151608567aaSSteve Twiss
152608567aaSSteve Twiss static const struct of_device_id da9062_compatible_reg_id_table[] = {
153608567aaSSteve Twiss { .compatible = "dlg,da9062-thermal", .data = &da9062_config },
154608567aaSSteve Twiss { },
155608567aaSSteve Twiss };
156608567aaSSteve Twiss
157608567aaSSteve Twiss MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table);
158608567aaSSteve Twiss
da9062_thermal_probe(struct platform_device * pdev)159608567aaSSteve Twiss static int da9062_thermal_probe(struct platform_device *pdev)
160608567aaSSteve Twiss {
161608567aaSSteve Twiss struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
162608567aaSSteve Twiss struct da9062_thermal *thermal;
163608567aaSSteve Twiss const struct of_device_id *match;
164608567aaSSteve Twiss int ret = 0;
165608567aaSSteve Twiss
166608567aaSSteve Twiss match = of_match_node(da9062_compatible_reg_id_table,
167608567aaSSteve Twiss pdev->dev.of_node);
168608567aaSSteve Twiss if (!match)
169608567aaSSteve Twiss return -ENXIO;
170608567aaSSteve Twiss
171608567aaSSteve Twiss if (pdev->dev.of_node) {
172608567aaSSteve Twiss if (!of_property_read_u32(pdev->dev.of_node,
173608567aaSSteve Twiss "polling-delay-passive",
174608567aaSSteve Twiss &pp_tmp)) {
175608567aaSSteve Twiss if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD ||
176608567aaSSteve Twiss pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) {
177608567aaSSteve Twiss dev_warn(&pdev->dev,
178608567aaSSteve Twiss "Out-of-range polling period %d ms\n",
179608567aaSSteve Twiss pp_tmp);
180608567aaSSteve Twiss pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
181608567aaSSteve Twiss }
182608567aaSSteve Twiss }
183608567aaSSteve Twiss }
184608567aaSSteve Twiss
185608567aaSSteve Twiss thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal),
186608567aaSSteve Twiss GFP_KERNEL);
187608567aaSSteve Twiss if (!thermal) {
188608567aaSSteve Twiss ret = -ENOMEM;
189608567aaSSteve Twiss goto err;
190608567aaSSteve Twiss }
191608567aaSSteve Twiss
192608567aaSSteve Twiss thermal->config = match->data;
193608567aaSSteve Twiss thermal->hw = chip;
194608567aaSSteve Twiss thermal->dev = &pdev->dev;
195608567aaSSteve Twiss
196608567aaSSteve Twiss INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
197608567aaSSteve Twiss mutex_init(&thermal->lock);
198608567aaSSteve Twiss
199060b39d9SDaniel Lezcano thermal->zone = thermal_zone_device_register_with_trips(thermal->config->name,
200*4a62d588SRafael J. Wysocki trips, ARRAY_SIZE(trips), thermal,
201608567aaSSteve Twiss &da9062_thermal_ops, NULL, pp_tmp,
202608567aaSSteve Twiss 0);
203608567aaSSteve Twiss if (IS_ERR(thermal->zone)) {
204608567aaSSteve Twiss dev_err(&pdev->dev, "Cannot register thermal zone device\n");
205608567aaSSteve Twiss ret = PTR_ERR(thermal->zone);
206608567aaSSteve Twiss goto err;
207608567aaSSteve Twiss }
2087f4957beSAndrzej Pietrasiewicz ret = thermal_zone_device_enable(thermal->zone);
2097f4957beSAndrzej Pietrasiewicz if (ret) {
2107f4957beSAndrzej Pietrasiewicz dev_err(&pdev->dev, "Cannot enable thermal zone device\n");
2117f4957beSAndrzej Pietrasiewicz goto err_zone;
2127f4957beSAndrzej Pietrasiewicz }
213608567aaSSteve Twiss
214608567aaSSteve Twiss dev_dbg(&pdev->dev,
2150adad092SDaniel Lezcano "TJUNC temperature polling period set at %d ms\n", pp_tmp);
216608567aaSSteve Twiss
217608567aaSSteve Twiss ret = platform_get_irq_byname(pdev, "THERMAL");
2181ea252efSzhaoxiao if (ret < 0)
219608567aaSSteve Twiss goto err_zone;
2201ea252efSzhaoxiao
221608567aaSSteve Twiss thermal->irq = ret;
222608567aaSSteve Twiss
223608567aaSSteve Twiss ret = request_threaded_irq(thermal->irq, NULL,
224608567aaSSteve Twiss da9062_thermal_irq_handler,
225608567aaSSteve Twiss IRQF_TRIGGER_LOW | IRQF_ONESHOT,
226608567aaSSteve Twiss "THERMAL", thermal);
227608567aaSSteve Twiss if (ret) {
228608567aaSSteve Twiss dev_err(&pdev->dev,
229608567aaSSteve Twiss "Failed to request thermal device IRQ.\n");
230608567aaSSteve Twiss goto err_zone;
231608567aaSSteve Twiss }
232608567aaSSteve Twiss
233608567aaSSteve Twiss platform_set_drvdata(pdev, thermal);
234608567aaSSteve Twiss return 0;
235608567aaSSteve Twiss
236608567aaSSteve Twiss err_zone:
237608567aaSSteve Twiss thermal_zone_device_unregister(thermal->zone);
238608567aaSSteve Twiss err:
239608567aaSSteve Twiss return ret;
240608567aaSSteve Twiss }
241608567aaSSteve Twiss
da9062_thermal_remove(struct platform_device * pdev)2420d0f8b2cSUwe Kleine-König static void da9062_thermal_remove(struct platform_device *pdev)
243608567aaSSteve Twiss {
244608567aaSSteve Twiss struct da9062_thermal *thermal = platform_get_drvdata(pdev);
245608567aaSSteve Twiss
246608567aaSSteve Twiss free_irq(thermal->irq, thermal);
247608567aaSSteve Twiss cancel_delayed_work_sync(&thermal->work);
248608567aaSSteve Twiss thermal_zone_device_unregister(thermal->zone);
249608567aaSSteve Twiss }
250608567aaSSteve Twiss
251608567aaSSteve Twiss static struct platform_driver da9062_thermal_driver = {
252608567aaSSteve Twiss .probe = da9062_thermal_probe,
2530d0f8b2cSUwe Kleine-König .remove_new = da9062_thermal_remove,
254608567aaSSteve Twiss .driver = {
255608567aaSSteve Twiss .name = "da9062-thermal",
256608567aaSSteve Twiss .of_match_table = da9062_compatible_reg_id_table,
257608567aaSSteve Twiss },
258608567aaSSteve Twiss };
259608567aaSSteve Twiss
260608567aaSSteve Twiss module_platform_driver(da9062_thermal_driver);
261608567aaSSteve Twiss
262608567aaSSteve Twiss MODULE_AUTHOR("Steve Twiss");
263608567aaSSteve Twiss MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061");
264608567aaSSteve Twiss MODULE_LICENSE("GPL");
265608567aaSSteve Twiss MODULE_ALIAS("platform:da9062-thermal");
266