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