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 
44*0adad092SDaniel Lezcano static unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
45*0adad092SDaniel 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 
100*0adad092SDaniel Lezcano 		/*
101*0adad092SDaniel Lezcano 		 * pp_tmp is between 1s and 10s, so we can round the jiffies
102*0adad092SDaniel Lezcano 		 */
103*0adad092SDaniel 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,
200060b39d9SDaniel Lezcano 								trips, ARRAY_SIZE(trips), 0, 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,
215*0adad092SDaniel 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)242608567aaSSteve Twiss static int 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 	return 0;
250608567aaSSteve Twiss }
251608567aaSSteve Twiss 
252608567aaSSteve Twiss static struct platform_driver da9062_thermal_driver = {
253608567aaSSteve Twiss 	.probe	= da9062_thermal_probe,
254608567aaSSteve Twiss 	.remove	= da9062_thermal_remove,
255608567aaSSteve Twiss 	.driver	= {
256608567aaSSteve Twiss 		.name	= "da9062-thermal",
257608567aaSSteve Twiss 		.of_match_table = da9062_compatible_reg_id_table,
258608567aaSSteve Twiss 	},
259608567aaSSteve Twiss };
260608567aaSSteve Twiss 
261608567aaSSteve Twiss module_platform_driver(da9062_thermal_driver);
262608567aaSSteve Twiss 
263608567aaSSteve Twiss MODULE_AUTHOR("Steve Twiss");
264608567aaSSteve Twiss MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061");
265608567aaSSteve Twiss MODULE_LICENSE("GPL");
266608567aaSSteve Twiss MODULE_ALIAS("platform:da9062-thermal");
267