1 /** 2 * uniphier_thermal.c - Socionext UniPhier thermal driver 3 * 4 * Copyright 2014 Panasonic Corporation 5 * Copyright 2016-2017 Socionext Inc. 6 * All rights reserved. 7 * 8 * Author: 9 * Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 10 * 11 * This program is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 of 13 * the License as published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21 #include <linux/bitops.h> 22 #include <linux/interrupt.h> 23 #include <linux/mfd/syscon.h> 24 #include <linux/module.h> 25 #include <linux/of.h> 26 #include <linux/of_device.h> 27 #include <linux/platform_device.h> 28 #include <linux/regmap.h> 29 #include <linux/thermal.h> 30 31 #include "thermal_core.h" 32 33 /* 34 * block registers 35 * addresses are the offset from .block_base 36 */ 37 #define PVTCTLEN 0x0000 38 #define PVTCTLEN_EN BIT(0) 39 40 #define PVTCTLMODE 0x0004 41 #define PVTCTLMODE_MASK 0xf 42 #define PVTCTLMODE_TEMPMON 0x5 43 44 #define EMONREPEAT 0x0040 45 #define EMONREPEAT_ENDLESS BIT(24) 46 #define EMONREPEAT_PERIOD GENMASK(3, 0) 47 #define EMONREPEAT_PERIOD_1000000 0x9 48 49 /* 50 * common registers 51 * addresses are the offset from .map_base 52 */ 53 #define PVTCTLSEL 0x0900 54 #define PVTCTLSEL_MASK GENMASK(2, 0) 55 #define PVTCTLSEL_MONITOR 0 56 57 #define SETALERT0 0x0910 58 #define SETALERT1 0x0914 59 #define SETALERT2 0x0918 60 #define SETALERT_TEMP_OVF (GENMASK(7, 0) << 16) 61 #define SETALERT_TEMP_OVF_VALUE(val) (((val) & GENMASK(7, 0)) << 16) 62 #define SETALERT_EN BIT(0) 63 64 #define PMALERTINTCTL 0x0920 65 #define PMALERTINTCTL_CLR(ch) BIT(4 * (ch) + 2) 66 #define PMALERTINTCTL_SET(ch) BIT(4 * (ch) + 1) 67 #define PMALERTINTCTL_EN(ch) BIT(4 * (ch) + 0) 68 #define PMALERTINTCTL_MASK (GENMASK(10, 8) | GENMASK(6, 4) | \ 69 GENMASK(2, 0)) 70 71 #define TMOD 0x0928 72 #define TMOD_WIDTH 9 73 74 #define TMODCOEF 0x0e5c 75 76 #define TMODSETUP0_EN BIT(30) 77 #define TMODSETUP0_VAL(val) (((val) & GENMASK(13, 0)) << 16) 78 #define TMODSETUP1_EN BIT(15) 79 #define TMODSETUP1_VAL(val) ((val) & GENMASK(14, 0)) 80 81 /* SoC critical temperature */ 82 #define CRITICAL_TEMP_LIMIT (120 * 1000) 83 84 /* Max # of alert channels */ 85 #define ALERT_CH_NUM 3 86 87 /* SoC specific thermal sensor data */ 88 struct uniphier_tm_soc_data { 89 u32 map_base; 90 u32 block_base; 91 u32 tmod_setup_addr; 92 }; 93 94 struct uniphier_tm_dev { 95 struct regmap *regmap; 96 struct device *dev; 97 bool alert_en[ALERT_CH_NUM]; 98 struct thermal_zone_device *tz_dev; 99 const struct uniphier_tm_soc_data *data; 100 }; 101 102 static int uniphier_tm_initialize_sensor(struct uniphier_tm_dev *tdev) 103 { 104 struct regmap *map = tdev->regmap; 105 u32 val; 106 u32 tmod_calib[2]; 107 int ret; 108 109 /* stop PVT */ 110 regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, 111 PVTCTLEN_EN, 0); 112 113 /* 114 * Since SoC has a calibrated value that was set in advance, 115 * TMODCOEF shows non-zero and PVT refers the value internally. 116 * 117 * If TMODCOEF shows zero, the boards don't have the calibrated 118 * value, and the driver has to set default value from DT. 119 */ 120 ret = regmap_read(map, tdev->data->map_base + TMODCOEF, &val); 121 if (ret) 122 return ret; 123 if (!val) { 124 /* look for the default values in DT */ 125 ret = of_property_read_u32_array(tdev->dev->of_node, 126 "socionext,tmod-calibration", 127 tmod_calib, 128 ARRAY_SIZE(tmod_calib)); 129 if (ret) 130 return ret; 131 132 regmap_write(map, tdev->data->tmod_setup_addr, 133 TMODSETUP0_EN | TMODSETUP0_VAL(tmod_calib[0]) | 134 TMODSETUP1_EN | TMODSETUP1_VAL(tmod_calib[1])); 135 } 136 137 /* select temperature mode */ 138 regmap_write_bits(map, tdev->data->block_base + PVTCTLMODE, 139 PVTCTLMODE_MASK, PVTCTLMODE_TEMPMON); 140 141 /* set monitoring period */ 142 regmap_write_bits(map, tdev->data->block_base + EMONREPEAT, 143 EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD, 144 EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD_1000000); 145 146 /* set monitor mode */ 147 regmap_write_bits(map, tdev->data->map_base + PVTCTLSEL, 148 PVTCTLSEL_MASK, PVTCTLSEL_MONITOR); 149 150 return 0; 151 } 152 153 static void uniphier_tm_set_alert(struct uniphier_tm_dev *tdev, u32 ch, 154 u32 temp) 155 { 156 struct regmap *map = tdev->regmap; 157 158 /* set alert temperature */ 159 regmap_write_bits(map, tdev->data->map_base + SETALERT0 + (ch << 2), 160 SETALERT_EN | SETALERT_TEMP_OVF, 161 SETALERT_EN | 162 SETALERT_TEMP_OVF_VALUE(temp / 1000)); 163 } 164 165 static void uniphier_tm_enable_sensor(struct uniphier_tm_dev *tdev) 166 { 167 struct regmap *map = tdev->regmap; 168 int i; 169 u32 bits = 0; 170 171 for (i = 0; i < ALERT_CH_NUM; i++) 172 if (tdev->alert_en[i]) 173 bits |= PMALERTINTCTL_EN(i); 174 175 /* enable alert interrupt */ 176 regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL, 177 PMALERTINTCTL_MASK, bits); 178 179 /* start PVT */ 180 regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, 181 PVTCTLEN_EN, PVTCTLEN_EN); 182 183 usleep_range(700, 1500); /* The spec note says at least 700us */ 184 } 185 186 static void uniphier_tm_disable_sensor(struct uniphier_tm_dev *tdev) 187 { 188 struct regmap *map = tdev->regmap; 189 190 /* disable alert interrupt */ 191 regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL, 192 PMALERTINTCTL_MASK, 0); 193 194 /* stop PVT */ 195 regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, 196 PVTCTLEN_EN, 0); 197 198 usleep_range(1000, 2000); /* The spec note says at least 1ms */ 199 } 200 201 static int uniphier_tm_get_temp(void *data, int *out_temp) 202 { 203 struct uniphier_tm_dev *tdev = data; 204 struct regmap *map = tdev->regmap; 205 int ret; 206 u32 temp; 207 208 ret = regmap_read(map, tdev->data->map_base + TMOD, &temp); 209 if (ret) 210 return ret; 211 212 /* MSB of the TMOD field is a sign bit */ 213 *out_temp = sign_extend32(temp, TMOD_WIDTH - 1) * 1000; 214 215 return 0; 216 } 217 218 static const struct thermal_zone_of_device_ops uniphier_of_thermal_ops = { 219 .get_temp = uniphier_tm_get_temp, 220 }; 221 222 static void uniphier_tm_irq_clear(struct uniphier_tm_dev *tdev) 223 { 224 u32 mask = 0, bits = 0; 225 int i; 226 227 for (i = 0; i < ALERT_CH_NUM; i++) { 228 mask |= (PMALERTINTCTL_CLR(i) | PMALERTINTCTL_SET(i)); 229 bits |= PMALERTINTCTL_CLR(i); 230 } 231 232 /* clear alert interrupt */ 233 regmap_write_bits(tdev->regmap, 234 tdev->data->map_base + PMALERTINTCTL, mask, bits); 235 } 236 237 static irqreturn_t uniphier_tm_alarm_irq(int irq, void *_tdev) 238 { 239 struct uniphier_tm_dev *tdev = _tdev; 240 241 disable_irq_nosync(irq); 242 uniphier_tm_irq_clear(tdev); 243 244 return IRQ_WAKE_THREAD; 245 } 246 247 static irqreturn_t uniphier_tm_alarm_irq_thread(int irq, void *_tdev) 248 { 249 struct uniphier_tm_dev *tdev = _tdev; 250 251 thermal_zone_device_update(tdev->tz_dev, THERMAL_EVENT_UNSPECIFIED); 252 253 return IRQ_HANDLED; 254 } 255 256 static int uniphier_tm_probe(struct platform_device *pdev) 257 { 258 struct device *dev = &pdev->dev; 259 struct regmap *regmap; 260 struct device_node *parent; 261 struct uniphier_tm_dev *tdev; 262 const struct thermal_trip *trips; 263 int i, ret, irq, ntrips, crit_temp = INT_MAX; 264 265 tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL); 266 if (!tdev) 267 return -ENOMEM; 268 tdev->dev = dev; 269 270 tdev->data = of_device_get_match_data(dev); 271 if (WARN_ON(!tdev->data)) 272 return -EINVAL; 273 274 irq = platform_get_irq(pdev, 0); 275 if (irq < 0) 276 return irq; 277 278 /* get regmap from syscon node */ 279 parent = of_get_parent(dev->of_node); /* parent should be syscon node */ 280 regmap = syscon_node_to_regmap(parent); 281 of_node_put(parent); 282 if (IS_ERR(regmap)) { 283 dev_err(dev, "failed to get regmap (error %ld)\n", 284 PTR_ERR(regmap)); 285 return PTR_ERR(regmap); 286 } 287 tdev->regmap = regmap; 288 289 ret = uniphier_tm_initialize_sensor(tdev); 290 if (ret) { 291 dev_err(dev, "failed to initialize sensor\n"); 292 return ret; 293 } 294 295 ret = devm_request_threaded_irq(dev, irq, uniphier_tm_alarm_irq, 296 uniphier_tm_alarm_irq_thread, 297 0, "thermal", tdev); 298 if (ret) 299 return ret; 300 301 platform_set_drvdata(pdev, tdev); 302 303 tdev->tz_dev = devm_thermal_zone_of_sensor_register(dev, 0, tdev, 304 &uniphier_of_thermal_ops); 305 if (IS_ERR(tdev->tz_dev)) { 306 dev_err(dev, "failed to register sensor device\n"); 307 return PTR_ERR(tdev->tz_dev); 308 } 309 310 /* get trip points */ 311 trips = of_thermal_get_trip_points(tdev->tz_dev); 312 ntrips = of_thermal_get_ntrips(tdev->tz_dev); 313 if (ntrips > ALERT_CH_NUM) { 314 dev_err(dev, "thermal zone has too many trips\n"); 315 return -E2BIG; 316 } 317 318 /* set alert temperatures */ 319 for (i = 0; i < ntrips; i++) { 320 if (trips[i].type == THERMAL_TRIP_CRITICAL && 321 trips[i].temperature < crit_temp) 322 crit_temp = trips[i].temperature; 323 uniphier_tm_set_alert(tdev, i, trips[i].temperature); 324 tdev->alert_en[i] = true; 325 } 326 if (crit_temp > CRITICAL_TEMP_LIMIT) { 327 dev_err(dev, "critical trip is over limit(>%d), or not set\n", 328 CRITICAL_TEMP_LIMIT); 329 return -EINVAL; 330 } 331 332 uniphier_tm_enable_sensor(tdev); 333 334 return 0; 335 } 336 337 static int uniphier_tm_remove(struct platform_device *pdev) 338 { 339 struct uniphier_tm_dev *tdev = platform_get_drvdata(pdev); 340 341 /* disable sensor */ 342 uniphier_tm_disable_sensor(tdev); 343 344 return 0; 345 } 346 347 static const struct uniphier_tm_soc_data uniphier_pxs2_tm_data = { 348 .map_base = 0xe000, 349 .block_base = 0xe000, 350 .tmod_setup_addr = 0xe904, 351 }; 352 353 static const struct uniphier_tm_soc_data uniphier_ld20_tm_data = { 354 .map_base = 0xe000, 355 .block_base = 0xe800, 356 .tmod_setup_addr = 0xe938, 357 }; 358 359 static const struct of_device_id uniphier_tm_dt_ids[] = { 360 { 361 .compatible = "socionext,uniphier-pxs2-thermal", 362 .data = &uniphier_pxs2_tm_data, 363 }, 364 { 365 .compatible = "socionext,uniphier-ld20-thermal", 366 .data = &uniphier_ld20_tm_data, 367 }, 368 { /* sentinel */ } 369 }; 370 MODULE_DEVICE_TABLE(of, uniphier_tm_dt_ids); 371 372 static struct platform_driver uniphier_tm_driver = { 373 .probe = uniphier_tm_probe, 374 .remove = uniphier_tm_remove, 375 .driver = { 376 .name = "uniphier-thermal", 377 .of_match_table = uniphier_tm_dt_ids, 378 }, 379 }; 380 module_platform_driver(uniphier_tm_driver); 381 382 MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 383 MODULE_DESCRIPTION("UniPhier thermal driver"); 384 MODULE_LICENSE("GPL v2"); 385