1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Thermal device driver for DA9062 and DA9061 4 * Copyright (C) 2017 Dialog Semiconductor 5 */ 6 7 /* When over-temperature is reached, an interrupt from the device will be 8 * triggered. Following this event the interrupt will be disabled and 9 * periodic transmission of uevents (HOT trip point) should define the 10 * first level of temperature supervision. It is expected that any final 11 * implementation of the thermal driver will include a .notify() function 12 * to implement these uevents to userspace. 13 * 14 * These uevents are intended to indicate non-invasive temperature control 15 * of the system, where the necessary measures for cooling are the 16 * responsibility of the host software. Once the temperature falls again, 17 * the IRQ is re-enabled so the start of a new over-temperature event can 18 * be detected without constant software monitoring. 19 */ 20 21 #include <linux/errno.h> 22 #include <linux/interrupt.h> 23 #include <linux/module.h> 24 #include <linux/of.h> 25 #include <linux/platform_device.h> 26 #include <linux/regmap.h> 27 #include <linux/thermal.h> 28 #include <linux/workqueue.h> 29 30 #include <linux/mfd/da9062/core.h> 31 #include <linux/mfd/da9062/registers.h> 32 33 /* Minimum, maximum and default polling millisecond periods are provided 34 * here as an example. It is expected that any final implementation to also 35 * include a modification of these settings to match the required 36 * application. 37 */ 38 #define DA9062_DEFAULT_POLLING_MS_PERIOD 3000 39 #define DA9062_MAX_POLLING_MS_PERIOD 10000 40 #define DA9062_MIN_POLLING_MS_PERIOD 1000 41 42 #define DA9062_MILLI_CELSIUS(t) ((t) * 1000) 43 44 struct da9062_thermal_config { 45 const char *name; 46 }; 47 48 struct da9062_thermal { 49 struct da9062 *hw; 50 struct delayed_work work; 51 struct thermal_zone_device *zone; 52 enum thermal_device_mode mode; 53 struct mutex lock; /* protection for da9062_thermal temperature */ 54 int temperature; 55 int irq; 56 const struct da9062_thermal_config *config; 57 struct device *dev; 58 }; 59 60 static void da9062_thermal_poll_on(struct work_struct *work) 61 { 62 struct da9062_thermal *thermal = container_of(work, 63 struct da9062_thermal, 64 work.work); 65 unsigned long delay; 66 unsigned int val; 67 int ret; 68 69 /* clear E_TEMP */ 70 ret = regmap_write(thermal->hw->regmap, 71 DA9062AA_EVENT_B, 72 DA9062AA_E_TEMP_MASK); 73 if (ret < 0) { 74 dev_err(thermal->dev, 75 "Cannot clear the TJUNC temperature status\n"); 76 goto err_enable_irq; 77 } 78 79 /* Now read E_TEMP again: it is acting like a status bit. 80 * If over-temperature, then this status will be true. 81 * If not over-temperature, this status will be false. 82 */ 83 ret = regmap_read(thermal->hw->regmap, 84 DA9062AA_EVENT_B, 85 &val); 86 if (ret < 0) { 87 dev_err(thermal->dev, 88 "Cannot check the TJUNC temperature status\n"); 89 goto err_enable_irq; 90 } 91 92 if (val & DA9062AA_E_TEMP_MASK) { 93 mutex_lock(&thermal->lock); 94 thermal->temperature = DA9062_MILLI_CELSIUS(125); 95 mutex_unlock(&thermal->lock); 96 thermal_zone_device_update(thermal->zone, 97 THERMAL_EVENT_UNSPECIFIED); 98 99 delay = msecs_to_jiffies(thermal->zone->passive_delay); 100 queue_delayed_work(system_freezable_wq, &thermal->work, delay); 101 return; 102 } 103 104 mutex_lock(&thermal->lock); 105 thermal->temperature = DA9062_MILLI_CELSIUS(0); 106 mutex_unlock(&thermal->lock); 107 thermal_zone_device_update(thermal->zone, 108 THERMAL_EVENT_UNSPECIFIED); 109 110 err_enable_irq: 111 enable_irq(thermal->irq); 112 } 113 114 static irqreturn_t da9062_thermal_irq_handler(int irq, void *data) 115 { 116 struct da9062_thermal *thermal = data; 117 118 disable_irq_nosync(thermal->irq); 119 queue_delayed_work(system_freezable_wq, &thermal->work, 0); 120 121 return IRQ_HANDLED; 122 } 123 124 static int da9062_thermal_get_mode(struct thermal_zone_device *z, 125 enum thermal_device_mode *mode) 126 { 127 struct da9062_thermal *thermal = z->devdata; 128 *mode = thermal->mode; 129 return 0; 130 } 131 132 static int da9062_thermal_get_trip_type(struct thermal_zone_device *z, 133 int trip, 134 enum thermal_trip_type *type) 135 { 136 struct da9062_thermal *thermal = z->devdata; 137 138 switch (trip) { 139 case 0: 140 *type = THERMAL_TRIP_HOT; 141 break; 142 default: 143 dev_err(thermal->dev, 144 "Driver does not support more than 1 trip-wire\n"); 145 return -EINVAL; 146 } 147 148 return 0; 149 } 150 151 static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z, 152 int trip, 153 int *temp) 154 { 155 struct da9062_thermal *thermal = z->devdata; 156 157 switch (trip) { 158 case 0: 159 *temp = DA9062_MILLI_CELSIUS(125); 160 break; 161 default: 162 dev_err(thermal->dev, 163 "Driver does not support more than 1 trip-wire\n"); 164 return -EINVAL; 165 } 166 167 return 0; 168 } 169 170 static int da9062_thermal_get_temp(struct thermal_zone_device *z, 171 int *temp) 172 { 173 struct da9062_thermal *thermal = z->devdata; 174 175 mutex_lock(&thermal->lock); 176 *temp = thermal->temperature; 177 mutex_unlock(&thermal->lock); 178 179 return 0; 180 } 181 182 static struct thermal_zone_device_ops da9062_thermal_ops = { 183 .get_temp = da9062_thermal_get_temp, 184 .get_mode = da9062_thermal_get_mode, 185 .get_trip_type = da9062_thermal_get_trip_type, 186 .get_trip_temp = da9062_thermal_get_trip_temp, 187 }; 188 189 static const struct da9062_thermal_config da9062_config = { 190 .name = "da9062-thermal", 191 }; 192 193 static const struct of_device_id da9062_compatible_reg_id_table[] = { 194 { .compatible = "dlg,da9062-thermal", .data = &da9062_config }, 195 { }, 196 }; 197 198 MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table); 199 200 static int da9062_thermal_probe(struct platform_device *pdev) 201 { 202 struct da9062 *chip = dev_get_drvdata(pdev->dev.parent); 203 struct da9062_thermal *thermal; 204 unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; 205 const struct of_device_id *match; 206 int ret = 0; 207 208 match = of_match_node(da9062_compatible_reg_id_table, 209 pdev->dev.of_node); 210 if (!match) 211 return -ENXIO; 212 213 if (pdev->dev.of_node) { 214 if (!of_property_read_u32(pdev->dev.of_node, 215 "polling-delay-passive", 216 &pp_tmp)) { 217 if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD || 218 pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) { 219 dev_warn(&pdev->dev, 220 "Out-of-range polling period %d ms\n", 221 pp_tmp); 222 pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; 223 } 224 } 225 } 226 227 thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal), 228 GFP_KERNEL); 229 if (!thermal) { 230 ret = -ENOMEM; 231 goto err; 232 } 233 234 thermal->config = match->data; 235 thermal->hw = chip; 236 thermal->mode = THERMAL_DEVICE_ENABLED; 237 thermal->dev = &pdev->dev; 238 239 INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on); 240 mutex_init(&thermal->lock); 241 242 thermal->zone = thermal_zone_device_register(thermal->config->name, 243 1, 0, thermal, 244 &da9062_thermal_ops, NULL, pp_tmp, 245 0); 246 if (IS_ERR(thermal->zone)) { 247 dev_err(&pdev->dev, "Cannot register thermal zone device\n"); 248 ret = PTR_ERR(thermal->zone); 249 goto err; 250 } 251 252 dev_dbg(&pdev->dev, 253 "TJUNC temperature polling period set at %d ms\n", 254 thermal->zone->passive_delay); 255 256 ret = platform_get_irq_byname(pdev, "THERMAL"); 257 if (ret < 0) { 258 dev_err(&pdev->dev, "Failed to get platform IRQ.\n"); 259 goto err_zone; 260 } 261 thermal->irq = ret; 262 263 ret = request_threaded_irq(thermal->irq, NULL, 264 da9062_thermal_irq_handler, 265 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 266 "THERMAL", thermal); 267 if (ret) { 268 dev_err(&pdev->dev, 269 "Failed to request thermal device IRQ.\n"); 270 goto err_zone; 271 } 272 273 platform_set_drvdata(pdev, thermal); 274 return 0; 275 276 err_zone: 277 thermal_zone_device_unregister(thermal->zone); 278 err: 279 return ret; 280 } 281 282 static int da9062_thermal_remove(struct platform_device *pdev) 283 { 284 struct da9062_thermal *thermal = platform_get_drvdata(pdev); 285 286 free_irq(thermal->irq, thermal); 287 cancel_delayed_work_sync(&thermal->work); 288 thermal_zone_device_unregister(thermal->zone); 289 return 0; 290 } 291 292 static struct platform_driver da9062_thermal_driver = { 293 .probe = da9062_thermal_probe, 294 .remove = da9062_thermal_remove, 295 .driver = { 296 .name = "da9062-thermal", 297 .of_match_table = da9062_compatible_reg_id_table, 298 }, 299 }; 300 301 module_platform_driver(da9062_thermal_driver); 302 303 MODULE_AUTHOR("Steve Twiss"); 304 MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061"); 305 MODULE_LICENSE("GPL"); 306 MODULE_ALIAS("platform:da9062-thermal"); 307