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