/** * exynos_tc_volt - locks or frees vdd_arm, vdd_mif/int and vdd_g3d for * temperature compensation. * * This function limits or free voltage of cpufreq, busfreq, and mali driver * according to 2nd arguments. */ static int exynos_tc_volt(struct s5p_tmu_info *info, int enable) { struct s5p_platform_tmu *data; static int usage; int ret = 0; if (!info || !(info->dev)) return -EPERM; data = info->dev->platform_data; if (enable == usage) { pr_debug("TMU: already is %s.\n", enable ? "locked" : "unlocked"); return 0; } if (enable) { ret = exynos_cpufreq_lock(DVFS_LOCK_ID_TMU, info->cpulevel_tc); if (ret) goto err_lock; #ifdef CONFIG_BUSFREQ_OPP ret = dev_lock(info->bus_dev, info->dev, info->busfreq_tc); if (ret) goto err_lock; #endif #if defined(CONFIG_VIDEO_MALI400MP) ret = mali_voltage_lock_push(data->temp_compensate.g3d_volt); if (ret < 0) { pr_err("TMU: g3d_push error: %u uV\n", data->temp_compensate.g3d_volt); goto err_lock; } #endif } else { exynos_cpufreq_lock_free(DVFS_LOCK_ID_TMU); #ifdef CONFIG_BUSFREQ_OPP ret = dev_unlock(info->bus_dev, info->dev); if (ret) goto err_unlock; #endif #if defined(CONFIG_VIDEO_MALI400MP) ret = mali_voltage_lock_pop(); if (ret < 0) { pr_err("TMU: g3d_pop error\n"); goto err_unlock; } #endif } usage = enable; pr_info("TMU: %s is ok!\n", enable ? "lock" : "unlock"); return ret; err_lock: err_unlock: pr_err("TMU: %s is fail.\n", enable ? "lock" : "unlock"); return ret; }
static void exynos4_handler_tmu_state(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); struct s5p_tmu_info *info = container_of(delayed_work, struct s5p_tmu_info, polling); struct s5p_platform_tmu *data = info->dev->platform_data; unsigned int cur_temp; static int auto_refresh_changed; static int check_handle; int trend = 0; mutex_lock(&tmu_lock); cur_temp = get_curr_temp(info); trend = cur_temp - info->last_temperature; pr_debug("curr_temp = %d, temp_diff = %d\n", cur_temp, trend); switch (info->tmu_state) { case TMU_STATUS_TC: #if defined(CONFIG_TC_VOLTAGE) if (cur_temp >= data->ts.stop_tc) { if (check_handle & TC_VOLTAGE_FLAG) { exynos_cpufreq_lock_free(DVFS_LOCK_ID_TMU); #ifdef CONFIG_BUSFREQ_OPP if (dev_unlock(info->bus_dev, info->dev)) pr_err("TMU: dev_unlock error!\n"); #endif if (mali_voltage_lock_pop() < 0) pr_err("TMU: g3d_pop error\n"); check_handle &= ~(TC_VOLTAGE_FLAG); pr_info("change state: tc -> normal.\n"); } info->tmu_state = TMU_STATUS_NORMAL; } else if (cur_temp <= data->ts.start_tc) { if (!(check_handle & TC_VOLTAGE_FLAG)) { if (exynos_cpufreq_lock(DVFS_LOCK_ID_TMU, info->cpulevel_tc) < 0) pr_err("TMU: cpu_lock error!\n"); #ifdef CONFIG_BUSFREQ_OPP if (dev_lock(info->bus_dev, info->dev, info->busfreq_tc) < 0) pr_err("TMU: bus_lock error\n"); #endif if (mali_voltage_lock_push(data->temp_compensate.g3d_volt) < 0) pr_err("TMU: g3d_push error [%u] uV\n", data->temp_compensate.g3d_volt); check_handle |= TC_VOLTAGE_FLAG; } } #endif break; case TMU_STATUS_NORMAL: /* 1. change state: 1st-throttling */ if (cur_temp >= data->ts.start_1st_throttle) { info->tmu_state = TMU_STATUS_THROTTLED; pr_info("change state: normal->throttle.\n"); /* 2. polling end and uevent */ #if defined(CONFIG_TC_VOLTAGE) } else if ((cur_temp <= data->ts.stop_1st_throttle) && (cur_temp >= data->ts.stop_tc) && (cur_temp <= data->ts.stop_mem_throttle)) { #else } else if ((cur_temp <= data->ts.stop_1st_throttle) && (cur_temp <= data->ts.stop_mem_throttle)) { #endif if (check_handle & THROTTLE_FLAG) { exynos_cpufreq_upper_limit_free(DVFS_LOCK_ID_TMU); check_handle &= ~(THROTTLE_FLAG); } pr_debug("check_handle = %d\n", check_handle); notify_change_of_tmu_state(info); pr_info("normal: free cpufreq_limit & interrupt enable.\n"); /* clear to prevent from interfupt by peindig bit */ __raw_writel(INTCLEARALL, info->tmu_base + EXYNOS4_TMU_INTCLEAR); exynos_interrupt_enable(info, 1); enable_irq(info->irq); mutex_unlock(&tmu_lock); return; } break; case TMU_STATUS_THROTTLED: /* 1. change state: 2nd-throttling or warning */ if (cur_temp >= data->ts.start_2nd_throttle) { info->tmu_state = TMU_STATUS_WARNING; pr_info("change state: 1st throttle->2nd throttle.\n"); /* 2. cpufreq limitation and uevent */ } else if ((cur_temp >= data->ts.start_1st_throttle) && !(check_handle & THROTTLE_FLAG)) { if (check_handle & WARNING_FLAG) { exynos_cpufreq_upper_limit_free(DVFS_LOCK_ID_TMU); check_handle &= ~(WARNING_FLAG); } exynos_cpufreq_upper_limit(DVFS_LOCK_ID_TMU, info->cpufreq_level_1st_throttle); check_handle |= THROTTLE_FLAG; pr_debug("check_handle = %d\n", check_handle); notify_change_of_tmu_state(info); pr_info("throttling: set cpufreq upper limit.\n"); /* 3. change state: normal */ } else if ((cur_temp <= data->ts.stop_1st_throttle) && (trend < 0)) { info->tmu_state = TMU_STATUS_NORMAL; pr_info("change state: 1st throttle->normal.\n"); } break; case TMU_STATUS_WARNING: /* 1. change state: tripping */ if (cur_temp >= data->ts.start_tripping) { info->tmu_state = TMU_STATUS_TRIPPED; pr_info("change state: 2nd throttle->trip\n"); /* 2. cpufreq limitation and uevent */ } else if ((cur_temp >= data->ts.start_2nd_throttle) && !(check_handle & WARNING_FLAG)) { if (check_handle & THROTTLE_FLAG) { exynos_cpufreq_upper_limit_free(DVFS_LOCK_ID_TMU); check_handle &= ~(THROTTLE_FLAG); } exynos_cpufreq_upper_limit(DVFS_LOCK_ID_TMU, info->cpufreq_level_2nd_throttle); check_handle |= WARNING_FLAG; pr_debug("check_handle = %d\n", check_handle); notify_change_of_tmu_state(info); pr_info("2nd throttle: cpufreq is limited.\n"); /* 3. change state: 1st-throttling */ } else if ((cur_temp <= data->ts.stop_2nd_throttle) && (trend < 0)) { info->tmu_state = TMU_STATUS_THROTTLED; pr_info("change state: 2nd throttle->1st throttle, " "and release cpufreq upper limit.\n"); } break; case TMU_STATUS_TRIPPED: /* 1. call uevent to shut-down */ if ((cur_temp >= data->ts.start_tripping) && (trend > 0) && !(check_handle & TRIPPING_FLAG)) { notify_change_of_tmu_state(info); pr_info("tripping: on waiting shutdown.\n"); check_handle |= TRIPPING_FLAG; pr_debug("check_handle = %d\n", check_handle); /* 2. change state: 2nd-throttling or warning */ } else if ((cur_temp <= data->ts.stop_2nd_throttle) && (trend < 0)) { info->tmu_state = TMU_STATUS_WARNING; pr_info("change state: trip->2nd throttle, " "Check! occured only test mode.\n"); } /* 3. chip protection: kernel panic as SW workaround */ if ((cur_temp >= data->ts.start_emergency) && (trend > 0)) { panic("Emergency!!!! tripping is not treated!\n"); /* clear to prevent from interfupt by peindig bit */ __raw_writel(INTCLEARALL, info->tmu_state + EXYNOS4_TMU_INTCLEAR); enable_irq(info->irq); mutex_unlock(&tmu_lock); return; } break; case TMU_STATUS_INIT: /* sned tmu initial status to platform */ disable_irq(info->irq); if (cur_temp >= data->ts.start_tripping) info->tmu_state = TMU_STATUS_TRIPPED; #if defined(CONFIG_TC_VOLTAGE) else if (cur_temp >= data->ts.start_tc) info->tmu_state = TMU_STATUS_TC; #endif else if (cur_temp >= data->ts.start_2nd_throttle) info->tmu_state = TMU_STATUS_WARNING; else if (cur_temp >= data->ts.start_1st_throttle) info->tmu_state = TMU_STATUS_THROTTLED; else if (cur_temp <= data->ts.stop_1st_throttle) info->tmu_state = TMU_STATUS_NORMAL; notify_change_of_tmu_state(info); pr_info("%s: inform to init state to platform.\n", __func__); break; default: pr_warn("Bug: checked tmu_state.\n"); if (cur_temp >= data->ts.start_tripping) info->tmu_state = TMU_STATUS_TRIPPED; else info->tmu_state = TMU_STATUS_WARNING; break; } /* end */ /* memory throttling */ if (cur_temp >= data->ts.start_mem_throttle) { if (!(auto_refresh_changed) && (trend > 0)) { pr_info("set auto_refresh 1.95us\n"); set_refresh_rate(info->auto_refresh_tq0); auto_refresh_changed = 1; } } else if (cur_temp <= (data->ts.stop_mem_throttle)) { if ((auto_refresh_changed) && (trend < 0)) { pr_info("set auto_refresh 3.9us\n"); set_refresh_rate(info->auto_refresh_normal); auto_refresh_changed = 0; } } info->last_temperature = cur_temp; /* reschedule the next work */ queue_delayed_work_on(0, tmu_monitor_wq, &info->polling, info->sampling_rate); mutex_unlock(&tmu_lock); return; }