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