exynos_tmu.c revision 4de0bdaa9677d11406c9becb70c60887c957e1f0
1/* 2 * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) 3 * 4 * Copyright (C) 2011 Samsung Electronics 5 * Donggeun Kim <dg77.kim@samsung.com> 6 * Amit Daniel Kachhap <amit.kachhap@linaro.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24#include <linux/clk.h> 25#include <linux/io.h> 26#include <linux/interrupt.h> 27#include <linux/module.h> 28#include <linux/of.h> 29#include <linux/platform_device.h> 30 31#include "exynos_thermal_common.h" 32#include "exynos_tmu.h" 33#include "exynos_tmu_data.h" 34 35struct exynos_tmu_data { 36 struct exynos_tmu_platform_data *pdata; 37 struct resource *mem; 38 void __iomem *base; 39 int irq; 40 enum soc_type soc; 41 struct work_struct irq_work; 42 struct mutex lock; 43 struct clk *clk; 44 u8 temp_error1, temp_error2; 45}; 46 47/* 48 * TMU treats temperature as a mapped temperature code. 49 * The temperature is converted differently depending on the calibration type. 50 */ 51static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 52{ 53 struct exynos_tmu_platform_data *pdata = data->pdata; 54 int temp_code; 55 56 if (data->soc == SOC_ARCH_EXYNOS4210) 57 /* temp should range between 25 and 125 */ 58 if (temp < 25 || temp > 125) { 59 temp_code = -EINVAL; 60 goto out; 61 } 62 63 switch (pdata->cal_type) { 64 case TYPE_TWO_POINT_TRIMMING: 65 temp_code = (temp - pdata->first_point_trim) * 66 (data->temp_error2 - data->temp_error1) / 67 (pdata->second_point_trim - pdata->first_point_trim) + 68 data->temp_error1; 69 break; 70 case TYPE_ONE_POINT_TRIMMING: 71 temp_code = temp + data->temp_error1 - pdata->first_point_trim; 72 break; 73 default: 74 temp_code = temp + pdata->default_temp_offset; 75 break; 76 } 77out: 78 return temp_code; 79} 80 81/* 82 * Calculate a temperature value from a temperature code. 83 * The unit of the temperature is degree Celsius. 84 */ 85static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) 86{ 87 struct exynos_tmu_platform_data *pdata = data->pdata; 88 int temp; 89 90 if (data->soc == SOC_ARCH_EXYNOS4210) 91 /* temp_code should range between 75 and 175 */ 92 if (temp_code < 75 || temp_code > 175) { 93 temp = -ENODATA; 94 goto out; 95 } 96 97 switch (pdata->cal_type) { 98 case TYPE_TWO_POINT_TRIMMING: 99 temp = (temp_code - data->temp_error1) * 100 (pdata->second_point_trim - pdata->first_point_trim) / 101 (data->temp_error2 - data->temp_error1) + 102 pdata->first_point_trim; 103 break; 104 case TYPE_ONE_POINT_TRIMMING: 105 temp = temp_code - data->temp_error1 + pdata->first_point_trim; 106 break; 107 default: 108 temp = temp_code - pdata->default_temp_offset; 109 break; 110 } 111out: 112 return temp; 113} 114 115static int exynos_tmu_initialize(struct platform_device *pdev) 116{ 117 struct exynos_tmu_data *data = platform_get_drvdata(pdev); 118 struct exynos_tmu_platform_data *pdata = data->pdata; 119 const struct exynos_tmu_registers *reg = pdata->registers; 120 unsigned int status, trim_info = 0, con; 121 unsigned int rising_threshold = 0, falling_threshold = 0; 122 int ret = 0, threshold_code, i, trigger_levs = 0; 123 124 mutex_lock(&data->lock); 125 clk_enable(data->clk); 126 127 status = readb(data->base + reg->tmu_status); 128 if (!status) { 129 ret = -EBUSY; 130 goto out; 131 } 132 133 if (data->soc == SOC_ARCH_EXYNOS) 134 __raw_writel(1, data->base + reg->triminfo_ctrl); 135 136 /* Save trimming info in order to perform calibration */ 137 trim_info = readl(data->base + reg->triminfo_data); 138 data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; 139 data->temp_error2 = ((trim_info >> reg->triminfo_85_shift) & 140 EXYNOS_TMU_TEMP_MASK); 141 142 if ((pdata->min_efuse_value > data->temp_error1) || 143 (data->temp_error1 > pdata->max_efuse_value) || 144 (data->temp_error2 != 0)) 145 data->temp_error1 = pdata->efuse_value; 146 147 if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) { 148 dev_err(&pdev->dev, "Invalid max trigger level\n"); 149 goto out; 150 } 151 152 for (i = 0; i < pdata->max_trigger_level; i++) { 153 if (!pdata->trigger_levels[i]) 154 continue; 155 156 if ((pdata->trigger_type[i] == HW_TRIP) && 157 (!pdata->trigger_levels[pdata->max_trigger_level - 1])) { 158 dev_err(&pdev->dev, "Invalid hw trigger level\n"); 159 ret = -EINVAL; 160 goto out; 161 } 162 163 /* Count trigger levels except the HW trip*/ 164 if (!(pdata->trigger_type[i] == HW_TRIP)) 165 trigger_levs++; 166 } 167 168 if (data->soc == SOC_ARCH_EXYNOS4210) { 169 /* Write temperature code for threshold */ 170 threshold_code = temp_to_code(data, pdata->threshold); 171 if (threshold_code < 0) { 172 ret = threshold_code; 173 goto out; 174 } 175 writeb(threshold_code, 176 data->base + reg->threshold_temp); 177 for (i = 0; i < trigger_levs; i++) 178 writeb(pdata->trigger_levels[i], data->base + 179 reg->threshold_th0 + i * sizeof(reg->threshold_th0)); 180 181 writel(reg->inten_rise_mask, data->base + reg->tmu_intclear); 182 } else if (data->soc == SOC_ARCH_EXYNOS) { 183 /* Write temperature code for rising and falling threshold */ 184 for (i = 0; 185 i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) { 186 threshold_code = temp_to_code(data, 187 pdata->trigger_levels[i]); 188 if (threshold_code < 0) { 189 ret = threshold_code; 190 goto out; 191 } 192 rising_threshold |= threshold_code << 8 * i; 193 if (pdata->threshold_falling) { 194 threshold_code = temp_to_code(data, 195 pdata->trigger_levels[i] - 196 pdata->threshold_falling); 197 if (threshold_code > 0) 198 falling_threshold |= 199 threshold_code << 8 * i; 200 } 201 } 202 203 writel(rising_threshold, 204 data->base + reg->threshold_th0); 205 writel(falling_threshold, 206 data->base + reg->threshold_th1); 207 208 writel((reg->inten_rise_mask << reg->inten_rise_shift) | 209 (reg->inten_fall_mask << reg->inten_fall_shift), 210 data->base + reg->tmu_intclear); 211 212 /* if last threshold limit is also present */ 213 i = pdata->max_trigger_level - 1; 214 if (pdata->trigger_levels[i] && 215 (pdata->trigger_type[i] == HW_TRIP)) { 216 threshold_code = temp_to_code(data, 217 pdata->trigger_levels[i]); 218 if (threshold_code < 0) { 219 ret = threshold_code; 220 goto out; 221 } 222 rising_threshold |= threshold_code << 8 * i; 223 writel(rising_threshold, 224 data->base + reg->threshold_th0); 225 con = readl(data->base + reg->tmu_ctrl); 226 con |= (1 << reg->therm_trip_en_shift); 227 writel(con, data->base + reg->tmu_ctrl); 228 } 229 } 230out: 231 clk_disable(data->clk); 232 mutex_unlock(&data->lock); 233 234 return ret; 235} 236 237static void exynos_tmu_control(struct platform_device *pdev, bool on) 238{ 239 struct exynos_tmu_data *data = platform_get_drvdata(pdev); 240 struct exynos_tmu_platform_data *pdata = data->pdata; 241 const struct exynos_tmu_registers *reg = pdata->registers; 242 unsigned int con, interrupt_en; 243 244 mutex_lock(&data->lock); 245 clk_enable(data->clk); 246 247 con = readl(data->base + reg->tmu_ctrl); 248 249 if (pdata->reference_voltage) { 250 con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift); 251 con |= pdata->reference_voltage << reg->buf_vref_sel_shift; 252 } 253 254 if (pdata->gain) { 255 con &= ~(reg->buf_slope_sel_mask << reg->buf_slope_sel_shift); 256 con |= (pdata->gain << reg->buf_slope_sel_shift); 257 } 258 259 if (pdata->noise_cancel_mode) { 260 con &= ~(reg->therm_trip_mode_mask << 261 reg->therm_trip_mode_shift); 262 con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift); 263 } 264 265 if (on) { 266 con |= (1 << reg->core_en_shift); 267 interrupt_en = 268 pdata->trigger_enable[3] << reg->inten_rise3_shift | 269 pdata->trigger_enable[2] << reg->inten_rise2_shift | 270 pdata->trigger_enable[1] << reg->inten_rise1_shift | 271 pdata->trigger_enable[0] << reg->inten_rise0_shift; 272 if (pdata->threshold_falling) 273 interrupt_en |= 274 interrupt_en << reg->inten_fall0_shift; 275 } else { 276 con &= ~(1 << reg->core_en_shift); 277 interrupt_en = 0; /* Disable all interrupts */ 278 } 279 writel(interrupt_en, data->base + reg->tmu_inten); 280 writel(con, data->base + reg->tmu_ctrl); 281 282 clk_disable(data->clk); 283 mutex_unlock(&data->lock); 284} 285 286static int exynos_tmu_read(struct exynos_tmu_data *data) 287{ 288 struct exynos_tmu_platform_data *pdata = data->pdata; 289 const struct exynos_tmu_registers *reg = pdata->registers; 290 u8 temp_code; 291 int temp; 292 293 mutex_lock(&data->lock); 294 clk_enable(data->clk); 295 296 temp_code = readb(data->base + reg->tmu_cur_temp); 297 temp = code_to_temp(data, temp_code); 298 299 clk_disable(data->clk); 300 mutex_unlock(&data->lock); 301 302 return temp; 303} 304 305#ifdef CONFIG_THERMAL_EMULATION 306static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) 307{ 308 struct exynos_tmu_data *data = drv_data; 309 struct exynos_tmu_platform_data *pdata = data->pdata; 310 const struct exynos_tmu_registers *reg = pdata->registers; 311 unsigned int val; 312 int ret = -EINVAL; 313 314 if (data->soc == SOC_ARCH_EXYNOS4210) 315 goto out; 316 317 if (temp && temp < MCELSIUS) 318 goto out; 319 320 mutex_lock(&data->lock); 321 clk_enable(data->clk); 322 323 val = readl(data->base + reg->emul_con); 324 325 if (temp) { 326 temp /= MCELSIUS; 327 328 val = (EXYNOS_EMUL_TIME << reg->emul_time_shift) | 329 (temp_to_code(data, temp) 330 << reg->emul_temp_shift) | EXYNOS_EMUL_ENABLE; 331 } else { 332 val &= ~EXYNOS_EMUL_ENABLE; 333 } 334 335 writel(val, data->base + reg->emul_con); 336 337 clk_disable(data->clk); 338 mutex_unlock(&data->lock); 339 return 0; 340out: 341 return ret; 342} 343#else 344static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) 345 { return -EINVAL; } 346#endif/*CONFIG_THERMAL_EMULATION*/ 347 348static struct thermal_sensor_conf exynos_sensor_conf = { 349 .name = "exynos-therm", 350 .read_temperature = (int (*)(void *))exynos_tmu_read, 351 .write_emul_temp = exynos_tmu_set_emulation, 352}; 353 354static void exynos_tmu_work(struct work_struct *work) 355{ 356 struct exynos_tmu_data *data = container_of(work, 357 struct exynos_tmu_data, irq_work); 358 struct exynos_tmu_platform_data *pdata = data->pdata; 359 const struct exynos_tmu_registers *reg = pdata->registers; 360 unsigned int val_irq; 361 362 exynos_report_trigger(&exynos_sensor_conf); 363 mutex_lock(&data->lock); 364 clk_enable(data->clk); 365 366 /* TODO: take action based on particular interrupt */ 367 val_irq = readl(data->base + reg->tmu_intstat); 368 /* clear the interrupts */ 369 writel(val_irq, data->base + reg->tmu_intclear); 370 371 clk_disable(data->clk); 372 mutex_unlock(&data->lock); 373 374 enable_irq(data->irq); 375} 376 377static irqreturn_t exynos_tmu_irq(int irq, void *id) 378{ 379 struct exynos_tmu_data *data = id; 380 381 disable_irq_nosync(irq); 382 schedule_work(&data->irq_work); 383 384 return IRQ_HANDLED; 385} 386 387#ifdef CONFIG_OF 388static const struct of_device_id exynos_tmu_match[] = { 389 { 390 .compatible = "samsung,exynos4210-tmu", 391 .data = (void *)EXYNOS4210_TMU_DRV_DATA, 392 }, 393 { 394 .compatible = "samsung,exynos4412-tmu", 395 .data = (void *)EXYNOS5250_TMU_DRV_DATA, 396 }, 397 { 398 .compatible = "samsung,exynos5250-tmu", 399 .data = (void *)EXYNOS5250_TMU_DRV_DATA, 400 }, 401 {}, 402}; 403MODULE_DEVICE_TABLE(of, exynos_tmu_match); 404#endif 405 406static struct platform_device_id exynos_tmu_driver_ids[] = { 407 { 408 .name = "exynos4210-tmu", 409 .driver_data = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA, 410 }, 411 { 412 .name = "exynos5250-tmu", 413 .driver_data = (kernel_ulong_t)EXYNOS5250_TMU_DRV_DATA, 414 }, 415 { }, 416}; 417MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids); 418 419static inline struct exynos_tmu_platform_data *exynos_get_driver_data( 420 struct platform_device *pdev) 421{ 422#ifdef CONFIG_OF 423 if (pdev->dev.of_node) { 424 const struct of_device_id *match; 425 match = of_match_node(exynos_tmu_match, pdev->dev.of_node); 426 if (!match) 427 return NULL; 428 return (struct exynos_tmu_platform_data *) match->data; 429 } 430#endif 431 return (struct exynos_tmu_platform_data *) 432 platform_get_device_id(pdev)->driver_data; 433} 434 435static int exynos_tmu_probe(struct platform_device *pdev) 436{ 437 struct exynos_tmu_data *data; 438 struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; 439 int ret, i; 440 441 if (!pdata) 442 pdata = exynos_get_driver_data(pdev); 443 444 if (!pdata) { 445 dev_err(&pdev->dev, "No platform init data supplied.\n"); 446 return -ENODEV; 447 } 448 data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 449 GFP_KERNEL); 450 if (!data) { 451 dev_err(&pdev->dev, "Failed to allocate driver structure\n"); 452 return -ENOMEM; 453 } 454 455 data->irq = platform_get_irq(pdev, 0); 456 if (data->irq < 0) { 457 dev_err(&pdev->dev, "Failed to get platform irq\n"); 458 return data->irq; 459 } 460 461 INIT_WORK(&data->irq_work, exynos_tmu_work); 462 463 data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 464 data->base = devm_ioremap_resource(&pdev->dev, data->mem); 465 if (IS_ERR(data->base)) 466 return PTR_ERR(data->base); 467 468 ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 469 IRQF_TRIGGER_RISING, "exynos-tmu", data); 470 if (ret) { 471 dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 472 return ret; 473 } 474 475 data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 476 if (IS_ERR(data->clk)) { 477 dev_err(&pdev->dev, "Failed to get clock\n"); 478 return PTR_ERR(data->clk); 479 } 480 481 ret = clk_prepare(data->clk); 482 if (ret) 483 return ret; 484 485 if (pdata->type == SOC_ARCH_EXYNOS || 486 pdata->type == SOC_ARCH_EXYNOS4210) 487 data->soc = pdata->type; 488 else { 489 ret = -EINVAL; 490 dev_err(&pdev->dev, "Platform not supported\n"); 491 goto err_clk; 492 } 493 494 data->pdata = pdata; 495 platform_set_drvdata(pdev, data); 496 mutex_init(&data->lock); 497 498 ret = exynos_tmu_initialize(pdev); 499 if (ret) { 500 dev_err(&pdev->dev, "Failed to initialize TMU\n"); 501 goto err_clk; 502 } 503 504 exynos_tmu_control(pdev, true); 505 506 /* Register the sensor with thermal management interface */ 507 (&exynos_sensor_conf)->private_data = data; 508 exynos_sensor_conf.trip_data.trip_count = pdata->trigger_enable[0] + 509 pdata->trigger_enable[1] + pdata->trigger_enable[2]+ 510 pdata->trigger_enable[3]; 511 512 for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++) 513 exynos_sensor_conf.trip_data.trip_val[i] = 514 pdata->threshold + pdata->trigger_levels[i]; 515 516 exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling; 517 518 exynos_sensor_conf.cooling_data.freq_clip_count = 519 pdata->freq_tab_count; 520 for (i = 0; i < pdata->freq_tab_count; i++) { 521 exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max = 522 pdata->freq_tab[i].freq_clip_max; 523 exynos_sensor_conf.cooling_data.freq_data[i].temp_level = 524 pdata->freq_tab[i].temp_level; 525 } 526 527 ret = exynos_register_thermal(&exynos_sensor_conf); 528 if (ret) { 529 dev_err(&pdev->dev, "Failed to register thermal interface\n"); 530 goto err_clk; 531 } 532 533 return 0; 534err_clk: 535 clk_unprepare(data->clk); 536 return ret; 537} 538 539static int exynos_tmu_remove(struct platform_device *pdev) 540{ 541 struct exynos_tmu_data *data = platform_get_drvdata(pdev); 542 543 exynos_tmu_control(pdev, false); 544 545 exynos_unregister_thermal(&exynos_sensor_conf); 546 547 clk_unprepare(data->clk); 548 549 return 0; 550} 551 552#ifdef CONFIG_PM_SLEEP 553static int exynos_tmu_suspend(struct device *dev) 554{ 555 exynos_tmu_control(to_platform_device(dev), false); 556 557 return 0; 558} 559 560static int exynos_tmu_resume(struct device *dev) 561{ 562 struct platform_device *pdev = to_platform_device(dev); 563 564 exynos_tmu_initialize(pdev); 565 exynos_tmu_control(pdev, true); 566 567 return 0; 568} 569 570static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 571 exynos_tmu_suspend, exynos_tmu_resume); 572#define EXYNOS_TMU_PM (&exynos_tmu_pm) 573#else 574#define EXYNOS_TMU_PM NULL 575#endif 576 577static struct platform_driver exynos_tmu_driver = { 578 .driver = { 579 .name = "exynos-tmu", 580 .owner = THIS_MODULE, 581 .pm = EXYNOS_TMU_PM, 582 .of_match_table = of_match_ptr(exynos_tmu_match), 583 }, 584 .probe = exynos_tmu_probe, 585 .remove = exynos_tmu_remove, 586 .id_table = exynos_tmu_driver_ids, 587}; 588 589module_platform_driver(exynos_tmu_driver); 590 591MODULE_DESCRIPTION("EXYNOS TMU Driver"); 592MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 593MODULE_LICENSE("GPL"); 594MODULE_ALIAS("platform:exynos-tmu"); 595