xref: /linux/drivers/thermal/qcom/qcom-spmi-mbg-tm.c (revision b81e34131907d2c243c51117a4aff8dbe22ccc9c)
1c3dce117SSatya Priya Kakitapalli // SPDX-License-Identifier: GPL-2.0-only
2c3dce117SSatya Priya Kakitapalli /*
3c3dce117SSatya Priya Kakitapalli  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4c3dce117SSatya Priya Kakitapalli  */
5c5f7c0d3SDaniel Lezcano #include <linux/bitfield.h>
6c3dce117SSatya Priya Kakitapalli #include <linux/interrupt.h>
7c3dce117SSatya Priya Kakitapalli #include <linux/irq.h>
8c3dce117SSatya Priya Kakitapalli #include <linux/module.h>
9c3dce117SSatya Priya Kakitapalli #include <linux/of.h>
10c3dce117SSatya Priya Kakitapalli #include <linux/platform_device.h>
11c3dce117SSatya Priya Kakitapalli #include <linux/regmap.h>
12c3dce117SSatya Priya Kakitapalli #include <linux/thermal.h>
13c3dce117SSatya Priya Kakitapalli #include <linux/iio/consumer.h>
14c3dce117SSatya Priya Kakitapalli 
15c3dce117SSatya Priya Kakitapalli #define MBG_TEMP_MON2_FAULT_STATUS	0x50
16c3dce117SSatya Priya Kakitapalli 
17c3dce117SSatya Priya Kakitapalli #define MON_FAULT_STATUS_MASK		GENMASK(7, 4)
18c3dce117SSatya Priya Kakitapalli #define MON_FAULT_LVL1_UPR		0x5
19c3dce117SSatya Priya Kakitapalli 
20c3dce117SSatya Priya Kakitapalli #define MON2_LVL1_UP_THRESH		0x59
21c3dce117SSatya Priya Kakitapalli 
22c3dce117SSatya Priya Kakitapalli #define MBG_TEMP_MON2_MISC_CFG		0x5f
23c3dce117SSatya Priya Kakitapalli #define MON2_UP_THRESH_EN		BIT(1)
24c3dce117SSatya Priya Kakitapalli 
25c3dce117SSatya Priya Kakitapalli #define MBG_TEMP_STEP_MV		8
26c3dce117SSatya Priya Kakitapalli #define MBG_TEMP_DEFAULT_TEMP_MV	600
27c3dce117SSatya Priya Kakitapalli #define MBG_TEMP_CONSTANT		1000
28c3dce117SSatya Priya Kakitapalli #define MBG_MIN_TRIP_TEMP		25000
29c3dce117SSatya Priya Kakitapalli #define MBG_MAX_SUPPORTED_TEMP		160000
30c3dce117SSatya Priya Kakitapalli 
31c3dce117SSatya Priya Kakitapalli /**
32c3dce117SSatya Priya Kakitapalli  * struct mbg_tm_chip - MBG thermal monitor device data.
33c3dce117SSatya Priya Kakitapalli  * @map: regmap for accessing MBG thermal registers.
34c3dce117SSatya Priya Kakitapalli  * @dev: mbg_tm_chip device.
35c3dce117SSatya Priya Kakitapalli  * @tz_dev: thermal zone device registered with the thermal framework.
36c3dce117SSatya Priya Kakitapalli  * @lock: mbg_tm_chip lock for set trip temperature.
37c3dce117SSatya Priya Kakitapalli  * @base: base register offset for this MBG instance
38c3dce117SSatya Priya Kakitapalli  * @irq: interrupt line used to signal threshold events
39c3dce117SSatya Priya Kakitapalli  * @last_temp: last measured temperature.
40c3dce117SSatya Priya Kakitapalli  * @last_thres_crossed: indicates whether the last interrupt crossed a threshold
41c3dce117SSatya Priya Kakitapalli  * @adc: IIO ADC channel used for temperature sensing
42c3dce117SSatya Priya Kakitapalli  */
43c3dce117SSatya Priya Kakitapalli struct mbg_tm_chip {
44c3dce117SSatya Priya Kakitapalli 	struct regmap			*map;
45c3dce117SSatya Priya Kakitapalli 	struct device			*dev;
46c3dce117SSatya Priya Kakitapalli 	struct thermal_zone_device	*tz_dev;
47c3dce117SSatya Priya Kakitapalli 	struct mutex                    lock;
48c3dce117SSatya Priya Kakitapalli 	unsigned int			base;
49c3dce117SSatya Priya Kakitapalli 	int				irq;
50c3dce117SSatya Priya Kakitapalli 	int				last_temp;
51c3dce117SSatya Priya Kakitapalli 	bool				last_thres_crossed;
52c3dce117SSatya Priya Kakitapalli 	struct iio_channel		*adc;
53c3dce117SSatya Priya Kakitapalli };
54c3dce117SSatya Priya Kakitapalli 
55c3dce117SSatya Priya Kakitapalli /**
56c3dce117SSatya Priya Kakitapalli  * struct mbg_map_table - temperature to voltage mapping entry
57c3dce117SSatya Priya Kakitapalli  * @min_temp: minimum temperature supported by this mapping entry
58c3dce117SSatya Priya Kakitapalli  * @vtemp0: reference voltage or ADC code corresponding to the temperature
59c3dce117SSatya Priya Kakitapalli  * @tc: temperature coefficient used for conversion calculations
60c3dce117SSatya Priya Kakitapalli  */
61c3dce117SSatya Priya Kakitapalli struct mbg_map_table {
62c3dce117SSatya Priya Kakitapalli 	int min_temp;
63c3dce117SSatya Priya Kakitapalli 	int vtemp0;
64c3dce117SSatya Priya Kakitapalli 	int tc;
65c3dce117SSatya Priya Kakitapalli };
66c3dce117SSatya Priya Kakitapalli 
67c3dce117SSatya Priya Kakitapalli static const struct mbg_map_table map_table[] = {
68c3dce117SSatya Priya Kakitapalli 	{ -60000, 4337, 1967 },
69c3dce117SSatya Priya Kakitapalli 	{ -40000, 4731, 1964 },
70c3dce117SSatya Priya Kakitapalli 	{ -20000, 5124, 1957 },
71c3dce117SSatya Priya Kakitapalli 	{ 0,      5515, 1949 },
72c3dce117SSatya Priya Kakitapalli 	{ 20000,  5905, 1940 },
73c3dce117SSatya Priya Kakitapalli 	{ 40000,  6293, 1930 },
74c3dce117SSatya Priya Kakitapalli 	{ 60000,  6679, 1921 },
75c3dce117SSatya Priya Kakitapalli 	{ 80000,  7064, 1910 },
76c3dce117SSatya Priya Kakitapalli 	{ 100000, 7446, 1896 },
77c3dce117SSatya Priya Kakitapalli 	{ 120000, 7825, 1878 },
78c3dce117SSatya Priya Kakitapalli 	{ 140000, 8201, 1859 },
79c3dce117SSatya Priya Kakitapalli };
80c3dce117SSatya Priya Kakitapalli 
mbg_tm_get_temp(struct thermal_zone_device * tz,int * temp)81c3dce117SSatya Priya Kakitapalli static int mbg_tm_get_temp(struct thermal_zone_device *tz, int *temp)
82c3dce117SSatya Priya Kakitapalli {
83c3dce117SSatya Priya Kakitapalli 	struct mbg_tm_chip *chip = thermal_zone_device_priv(tz);
84c3dce117SSatya Priya Kakitapalli 	int ret, milli_celsius;
85c3dce117SSatya Priya Kakitapalli 
86c3dce117SSatya Priya Kakitapalli 	scoped_guard(mutex, &chip->lock) {
87c3dce117SSatya Priya Kakitapalli 		if (chip->last_thres_crossed) {
88c3dce117SSatya Priya Kakitapalli 			dev_dbg(chip->dev, "last_temp: %d\n", chip->last_temp);
89c3dce117SSatya Priya Kakitapalli 			chip->last_thres_crossed = false;
90c3dce117SSatya Priya Kakitapalli 			*temp = chip->last_temp;
91c3dce117SSatya Priya Kakitapalli 			return 0;
92c3dce117SSatya Priya Kakitapalli 		}
93c3dce117SSatya Priya Kakitapalli 	}
94c3dce117SSatya Priya Kakitapalli 
95c3dce117SSatya Priya Kakitapalli 	ret = iio_read_channel_processed(chip->adc, &milli_celsius);
96c3dce117SSatya Priya Kakitapalli 	if (ret < 0) {
97c3dce117SSatya Priya Kakitapalli 		dev_err(chip->dev, "Failed to read iio channel with %d\n", ret);
98c3dce117SSatya Priya Kakitapalli 		return ret;
99c3dce117SSatya Priya Kakitapalli 	}
100c3dce117SSatya Priya Kakitapalli 
101c3dce117SSatya Priya Kakitapalli 	*temp = milli_celsius;
102c3dce117SSatya Priya Kakitapalli 
103c3dce117SSatya Priya Kakitapalli 	return 0;
104c3dce117SSatya Priya Kakitapalli }
105c3dce117SSatya Priya Kakitapalli 
temp_to_vtemp_mv(int temp)106c3dce117SSatya Priya Kakitapalli static int temp_to_vtemp_mv(int temp)
107c3dce117SSatya Priya Kakitapalli {
108c3dce117SSatya Priya Kakitapalli 	int idx, vtemp, tc = 0, t0 = 0, vtemp0 = 0;
109c3dce117SSatya Priya Kakitapalli 
110c3dce117SSatya Priya Kakitapalli 	for (idx = 0; idx < ARRAY_SIZE(map_table); idx++)
111c3dce117SSatya Priya Kakitapalli 		if (temp >= map_table[idx].min_temp &&
112c3dce117SSatya Priya Kakitapalli 		    temp < (map_table[idx].min_temp + 20000)) {
113c3dce117SSatya Priya Kakitapalli 			tc = map_table[idx].tc;
114c3dce117SSatya Priya Kakitapalli 			t0 = map_table[idx].min_temp;
115c3dce117SSatya Priya Kakitapalli 			vtemp0 = map_table[idx].vtemp0;
116c3dce117SSatya Priya Kakitapalli 			break;
117c3dce117SSatya Priya Kakitapalli 		}
118c3dce117SSatya Priya Kakitapalli 
119c3dce117SSatya Priya Kakitapalli 	/*
120c3dce117SSatya Priya Kakitapalli 	 * Formula to calculate vtemp(mV) from a given temp
121c3dce117SSatya Priya Kakitapalli 	 * vtemp = (temp - minT) * tc + vtemp0
122c3dce117SSatya Priya Kakitapalli 	 * tc, t0 and vtemp0 values are mentioned in the map_table array.
123c3dce117SSatya Priya Kakitapalli 	 */
124c3dce117SSatya Priya Kakitapalli 	vtemp = ((temp - t0) * tc + vtemp0 * 100000) / 1000000;
125c3dce117SSatya Priya Kakitapalli 
126c3dce117SSatya Priya Kakitapalli 	/* step size is 8mV */
127c3dce117SSatya Priya Kakitapalli 	return abs(vtemp - MBG_TEMP_DEFAULT_TEMP_MV) / MBG_TEMP_STEP_MV;
128c3dce117SSatya Priya Kakitapalli }
129c3dce117SSatya Priya Kakitapalli 
mbg_tm_set_trip_temp(struct thermal_zone_device * tz,int low_temp,int temp)130c3dce117SSatya Priya Kakitapalli static int mbg_tm_set_trip_temp(struct thermal_zone_device *tz, int low_temp,
131c3dce117SSatya Priya Kakitapalli 				int temp)
132c3dce117SSatya Priya Kakitapalli {
133c3dce117SSatya Priya Kakitapalli 	struct mbg_tm_chip *chip = thermal_zone_device_priv(tz);
134c3dce117SSatya Priya Kakitapalli 	int ret = 0;
135c3dce117SSatya Priya Kakitapalli 
136c3dce117SSatya Priya Kakitapalli 	guard(mutex)(&chip->lock);
137c3dce117SSatya Priya Kakitapalli 
138c3dce117SSatya Priya Kakitapalli 	/* The HW has a limitation that the trip set must be above 25C */
139c3dce117SSatya Priya Kakitapalli 	if (temp > MBG_MIN_TRIP_TEMP && temp < MBG_MAX_SUPPORTED_TEMP) {
140c3dce117SSatya Priya Kakitapalli 		ret = regmap_write(chip->map, chip->base + MON2_LVL1_UP_THRESH,
141c3dce117SSatya Priya Kakitapalli 				   temp_to_vtemp_mv(temp));
142c3dce117SSatya Priya Kakitapalli 		if (ret < 0)
143c3dce117SSatya Priya Kakitapalli 			return ret;
144c3dce117SSatya Priya Kakitapalli 
145c3dce117SSatya Priya Kakitapalli 		ret = regmap_set_bits(chip->map, chip->base + MBG_TEMP_MON2_MISC_CFG,
146c3dce117SSatya Priya Kakitapalli 				      MON2_UP_THRESH_EN);
147c3dce117SSatya Priya Kakitapalli 		if (ret < 0)
148c3dce117SSatya Priya Kakitapalli 			return ret;
149c3dce117SSatya Priya Kakitapalli 	} else {
150c3dce117SSatya Priya Kakitapalli 		dev_err(chip->dev, "Set trip b/w 25C and 160C\n");
151c3dce117SSatya Priya Kakitapalli 		ret = regmap_clear_bits(chip->map, chip->base + MBG_TEMP_MON2_MISC_CFG,
152c3dce117SSatya Priya Kakitapalli 					MON2_UP_THRESH_EN);
153c3dce117SSatya Priya Kakitapalli 		return -ERANGE;
154c3dce117SSatya Priya Kakitapalli 	}
155c3dce117SSatya Priya Kakitapalli 
156c3dce117SSatya Priya Kakitapalli 	/*
157c3dce117SSatya Priya Kakitapalli 	 * Configure the last_temp one degree higher, to ensure the
158c3dce117SSatya Priya Kakitapalli 	 * violated temp is returned to thermal framework when it reads
159c3dce117SSatya Priya Kakitapalli 	 * temperature for the first time after the violation happens.
160c3dce117SSatya Priya Kakitapalli 	 * This is needed to account for the inaccuracy in the conversion
161c3dce117SSatya Priya Kakitapalli 	 * formula used which leads to the thermal framework setting back
162c3dce117SSatya Priya Kakitapalli 	 * the same thresholds in case the temperature it reads does not
163c3dce117SSatya Priya Kakitapalli 	 * show violation.
164c3dce117SSatya Priya Kakitapalli 	 */
165c3dce117SSatya Priya Kakitapalli 	chip->last_temp = temp + MBG_TEMP_CONSTANT;
166c3dce117SSatya Priya Kakitapalli 
167c3dce117SSatya Priya Kakitapalli 	return ret;
168c3dce117SSatya Priya Kakitapalli }
169c3dce117SSatya Priya Kakitapalli 
170c3dce117SSatya Priya Kakitapalli static const struct thermal_zone_device_ops mbg_tm_ops = {
171c3dce117SSatya Priya Kakitapalli 	.get_temp = mbg_tm_get_temp,
172c3dce117SSatya Priya Kakitapalli 	.set_trips = mbg_tm_set_trip_temp,
173c3dce117SSatya Priya Kakitapalli };
174c3dce117SSatya Priya Kakitapalli 
mbg_tm_isr(int irq,void * data)175c3dce117SSatya Priya Kakitapalli static irqreturn_t mbg_tm_isr(int irq, void *data)
176c3dce117SSatya Priya Kakitapalli {
177c3dce117SSatya Priya Kakitapalli 	struct mbg_tm_chip *chip = data;
178c3dce117SSatya Priya Kakitapalli 	int ret, val;
179c3dce117SSatya Priya Kakitapalli 
180c3dce117SSatya Priya Kakitapalli 	scoped_guard(mutex, &chip->lock) {
181c3dce117SSatya Priya Kakitapalli 		ret = regmap_read(chip->map, chip->base + MBG_TEMP_MON2_FAULT_STATUS, &val);
182c3dce117SSatya Priya Kakitapalli 		if (ret < 0)
183c3dce117SSatya Priya Kakitapalli 			return IRQ_HANDLED;
184c3dce117SSatya Priya Kakitapalli 		if (FIELD_GET(MON_FAULT_STATUS_MASK, val) == MON_FAULT_LVL1_UPR)
185c3dce117SSatya Priya Kakitapalli 			chip->last_thres_crossed = true;
186c3dce117SSatya Priya Kakitapalli 	}
187c3dce117SSatya Priya Kakitapalli 
188c3dce117SSatya Priya Kakitapalli 	if (FIELD_GET(MON_FAULT_STATUS_MASK, val) == MON_FAULT_LVL1_UPR) {
189c3dce117SSatya Priya Kakitapalli 		dev_dbg(chip->dev, "Notifying Thermal, fault status=%d\n", val);
190c3dce117SSatya Priya Kakitapalli 		thermal_zone_device_update(chip->tz_dev, THERMAL_TRIP_VIOLATED);
191c3dce117SSatya Priya Kakitapalli 	} else {
192c3dce117SSatya Priya Kakitapalli 		dev_dbg(chip->dev, "Lvl1 upper threshold not violated, ignoring interrupt\n");
193c3dce117SSatya Priya Kakitapalli 	}
194c3dce117SSatya Priya Kakitapalli 
195c3dce117SSatya Priya Kakitapalli 	return IRQ_HANDLED;
196c3dce117SSatya Priya Kakitapalli }
197c3dce117SSatya Priya Kakitapalli 
mbg_tm_probe(struct platform_device * pdev)198c3dce117SSatya Priya Kakitapalli static int mbg_tm_probe(struct platform_device *pdev)
199c3dce117SSatya Priya Kakitapalli {
200c3dce117SSatya Priya Kakitapalli 	struct mbg_tm_chip *chip;
201c3dce117SSatya Priya Kakitapalli 	struct device_node *node = pdev->dev.of_node;
202c3dce117SSatya Priya Kakitapalli 	u32 res;
203c3dce117SSatya Priya Kakitapalli 	int ret;
204c3dce117SSatya Priya Kakitapalli 
205c3dce117SSatya Priya Kakitapalli 	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
206c3dce117SSatya Priya Kakitapalli 	if (!chip)
207c3dce117SSatya Priya Kakitapalli 		return -ENOMEM;
208c3dce117SSatya Priya Kakitapalli 
209c3dce117SSatya Priya Kakitapalli 	chip->dev = &pdev->dev;
210c3dce117SSatya Priya Kakitapalli 
211c3dce117SSatya Priya Kakitapalli 	mutex_init(&chip->lock);
212c3dce117SSatya Priya Kakitapalli 
213c3dce117SSatya Priya Kakitapalli 	chip->map = dev_get_regmap(pdev->dev.parent, NULL);
214c3dce117SSatya Priya Kakitapalli 	if (!chip->map)
215c3dce117SSatya Priya Kakitapalli 		return -ENXIO;
216c3dce117SSatya Priya Kakitapalli 
217c3dce117SSatya Priya Kakitapalli 	ret = device_property_read_u32(chip->dev, "reg", &res);
218c3dce117SSatya Priya Kakitapalli 	if (ret < 0)
219c3dce117SSatya Priya Kakitapalli 		return dev_err_probe(chip->dev, ret, "Couldn't read reg property\n");
220c3dce117SSatya Priya Kakitapalli 
221c3dce117SSatya Priya Kakitapalli 	chip->base = res;
222c3dce117SSatya Priya Kakitapalli 
223c3dce117SSatya Priya Kakitapalli 	chip->irq = platform_get_irq(pdev, 0);
224c3dce117SSatya Priya Kakitapalli 	if (chip->irq < 0)
225c3dce117SSatya Priya Kakitapalli 		return dev_err_probe(chip->dev, chip->irq, "Failed to get irq\n");
226c3dce117SSatya Priya Kakitapalli 
227c3dce117SSatya Priya Kakitapalli 	chip->adc = devm_iio_channel_get(&pdev->dev, "thermal");
228c3dce117SSatya Priya Kakitapalli 	if (IS_ERR(chip->adc))
229c3dce117SSatya Priya Kakitapalli 		return dev_err_probe(chip->dev, PTR_ERR(chip->adc), "Failed to get adc channel\n");
230c3dce117SSatya Priya Kakitapalli 
231c3dce117SSatya Priya Kakitapalli 	chip->tz_dev = devm_thermal_of_zone_register(chip->dev, 0, chip, &mbg_tm_ops);
232c3dce117SSatya Priya Kakitapalli 	if (IS_ERR(chip->tz_dev))
233c3dce117SSatya Priya Kakitapalli 		return dev_err_probe(chip->dev, PTR_ERR(chip->tz_dev),
234c3dce117SSatya Priya Kakitapalli 				     "Failed to register sensor\n");
235c3dce117SSatya Priya Kakitapalli 
236c3dce117SSatya Priya Kakitapalli 	return devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, mbg_tm_isr, IRQF_ONESHOT,
237c3dce117SSatya Priya Kakitapalli 					 node->name, chip);
238c3dce117SSatya Priya Kakitapalli }
239c3dce117SSatya Priya Kakitapalli 
240c3dce117SSatya Priya Kakitapalli static const struct of_device_id mbg_tm_match_table[] = {
241c3dce117SSatya Priya Kakitapalli 	{ .compatible = "qcom,pm8775-mbg-tm" },
242c3dce117SSatya Priya Kakitapalli 	{ }
243c3dce117SSatya Priya Kakitapalli };
244c3dce117SSatya Priya Kakitapalli MODULE_DEVICE_TABLE(of, mbg_tm_match_table);
245c3dce117SSatya Priya Kakitapalli 
246c3dce117SSatya Priya Kakitapalli static struct platform_driver mbg_tm_driver = {
247c3dce117SSatya Priya Kakitapalli 	.driver = {
248c3dce117SSatya Priya Kakitapalli 		.name = "qcom-spmi-mbg-tm",
249c3dce117SSatya Priya Kakitapalli 		.of_match_table = mbg_tm_match_table,
250c3dce117SSatya Priya Kakitapalli 	},
251c3dce117SSatya Priya Kakitapalli 	.probe = mbg_tm_probe,
252c3dce117SSatya Priya Kakitapalli };
253c3dce117SSatya Priya Kakitapalli module_platform_driver(mbg_tm_driver);
254c3dce117SSatya Priya Kakitapalli 
255c3dce117SSatya Priya Kakitapalli MODULE_DESCRIPTION("PMIC MBG Temperature monitor driver");
256c3dce117SSatya Priya Kakitapalli MODULE_LICENSE("GPL");
257*79a57e48SNathan Chancellor MODULE_IMPORT_NS("IIO_CONSUMER");
258