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 static unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; 45 46 struct da9062_thermal_config { 47 const char *name; 48 }; 49 50 struct da9062_thermal { 51 struct da9062 *hw; 52 struct delayed_work work; 53 struct thermal_zone_device *zone; 54 struct mutex lock; /* protection for da9062_thermal temperature */ 55 int temperature; 56 int irq; 57 const struct da9062_thermal_config *config; 58 struct device *dev; 59 }; 60 61 static void da9062_thermal_poll_on(struct work_struct *work) 62 { 63 struct da9062_thermal *thermal = container_of(work, 64 struct da9062_thermal, 65 work.work); 66 unsigned long delay; 67 unsigned int val; 68 int ret; 69 70 /* clear E_TEMP */ 71 ret = regmap_write(thermal->hw->regmap, 72 DA9062AA_EVENT_B, 73 DA9062AA_E_TEMP_MASK); 74 if (ret < 0) { 75 dev_err(thermal->dev, 76 "Cannot clear the TJUNC temperature status\n"); 77 goto err_enable_irq; 78 } 79 80 /* Now read E_TEMP again: it is acting like a status bit. 81 * If over-temperature, then this status will be true. 82 * If not over-temperature, this status will be false. 83 */ 84 ret = regmap_read(thermal->hw->regmap, 85 DA9062AA_EVENT_B, 86 &val); 87 if (ret < 0) { 88 dev_err(thermal->dev, 89 "Cannot check the TJUNC temperature status\n"); 90 goto err_enable_irq; 91 } 92 93 if (val & DA9062AA_E_TEMP_MASK) { 94 mutex_lock(&thermal->lock); 95 thermal->temperature = DA9062_MILLI_CELSIUS(125); 96 mutex_unlock(&thermal->lock); 97 thermal_zone_device_update(thermal->zone, 98 THERMAL_EVENT_UNSPECIFIED); 99 100 /* 101 * pp_tmp is between 1s and 10s, so we can round the jiffies 102 */ 103 delay = round_jiffies(msecs_to_jiffies(pp_tmp)); 104 queue_delayed_work(system_freezable_wq, &thermal->work, delay); 105 return; 106 } 107 108 mutex_lock(&thermal->lock); 109 thermal->temperature = DA9062_MILLI_CELSIUS(0); 110 mutex_unlock(&thermal->lock); 111 thermal_zone_device_update(thermal->zone, 112 THERMAL_EVENT_UNSPECIFIED); 113 114 err_enable_irq: 115 enable_irq(thermal->irq); 116 } 117 118 static irqreturn_t da9062_thermal_irq_handler(int irq, void *data) 119 { 120 struct da9062_thermal *thermal = data; 121 122 disable_irq_nosync(thermal->irq); 123 queue_delayed_work(system_freezable_wq, &thermal->work, 0); 124 125 return IRQ_HANDLED; 126 } 127 128 static int da9062_thermal_get_temp(struct thermal_zone_device *z, 129 int *temp) 130 { 131 struct da9062_thermal *thermal = thermal_zone_device_priv(z); 132 133 mutex_lock(&thermal->lock); 134 *temp = thermal->temperature; 135 mutex_unlock(&thermal->lock); 136 137 return 0; 138 } 139 140 static struct thermal_zone_device_ops da9062_thermal_ops = { 141 .get_temp = da9062_thermal_get_temp, 142 }; 143 144 static struct thermal_trip trips[] = { 145 { .temperature = DA9062_MILLI_CELSIUS(125), .type = THERMAL_TRIP_HOT }, 146 }; 147 148 static const struct da9062_thermal_config da9062_config = { 149 .name = "da9062-thermal", 150 }; 151 152 static const struct of_device_id da9062_compatible_reg_id_table[] = { 153 { .compatible = "dlg,da9062-thermal", .data = &da9062_config }, 154 { }, 155 }; 156 157 MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table); 158 159 static int da9062_thermal_probe(struct platform_device *pdev) 160 { 161 struct da9062 *chip = dev_get_drvdata(pdev->dev.parent); 162 struct da9062_thermal *thermal; 163 const struct of_device_id *match; 164 int ret = 0; 165 166 match = of_match_node(da9062_compatible_reg_id_table, 167 pdev->dev.of_node); 168 if (!match) 169 return -ENXIO; 170 171 if (pdev->dev.of_node) { 172 if (!of_property_read_u32(pdev->dev.of_node, 173 "polling-delay-passive", 174 &pp_tmp)) { 175 if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD || 176 pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) { 177 dev_warn(&pdev->dev, 178 "Out-of-range polling period %d ms\n", 179 pp_tmp); 180 pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; 181 } 182 } 183 } 184 185 thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal), 186 GFP_KERNEL); 187 if (!thermal) { 188 ret = -ENOMEM; 189 goto err; 190 } 191 192 thermal->config = match->data; 193 thermal->hw = chip; 194 thermal->dev = &pdev->dev; 195 196 INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on); 197 mutex_init(&thermal->lock); 198 199 thermal->zone = thermal_zone_device_register_with_trips(thermal->config->name, 200 trips, ARRAY_SIZE(trips), 0, thermal, 201 &da9062_thermal_ops, NULL, pp_tmp, 202 0); 203 if (IS_ERR(thermal->zone)) { 204 dev_err(&pdev->dev, "Cannot register thermal zone device\n"); 205 ret = PTR_ERR(thermal->zone); 206 goto err; 207 } 208 ret = thermal_zone_device_enable(thermal->zone); 209 if (ret) { 210 dev_err(&pdev->dev, "Cannot enable thermal zone device\n"); 211 goto err_zone; 212 } 213 214 dev_dbg(&pdev->dev, 215 "TJUNC temperature polling period set at %d ms\n", pp_tmp); 216 217 ret = platform_get_irq_byname(pdev, "THERMAL"); 218 if (ret < 0) 219 goto err_zone; 220 221 thermal->irq = ret; 222 223 ret = request_threaded_irq(thermal->irq, NULL, 224 da9062_thermal_irq_handler, 225 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 226 "THERMAL", thermal); 227 if (ret) { 228 dev_err(&pdev->dev, 229 "Failed to request thermal device IRQ.\n"); 230 goto err_zone; 231 } 232 233 platform_set_drvdata(pdev, thermal); 234 return 0; 235 236 err_zone: 237 thermal_zone_device_unregister(thermal->zone); 238 err: 239 return ret; 240 } 241 242 static int da9062_thermal_remove(struct platform_device *pdev) 243 { 244 struct da9062_thermal *thermal = platform_get_drvdata(pdev); 245 246 free_irq(thermal->irq, thermal); 247 cancel_delayed_work_sync(&thermal->work); 248 thermal_zone_device_unregister(thermal->zone); 249 return 0; 250 } 251 252 static struct platform_driver da9062_thermal_driver = { 253 .probe = da9062_thermal_probe, 254 .remove = da9062_thermal_remove, 255 .driver = { 256 .name = "da9062-thermal", 257 .of_match_table = da9062_compatible_reg_id_table, 258 }, 259 }; 260 261 module_platform_driver(da9062_thermal_driver); 262 263 MODULE_AUTHOR("Steve Twiss"); 264 MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061"); 265 MODULE_LICENSE("GPL"); 266 MODULE_ALIAS("platform:da9062-thermal"); 267