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