/** * df_rgx_is_valid_freq() - Determines if We are about to use * a valid frequency. * @freq: frequency to be validated. * * Function return value: 1 if Valid 0 if not. */ unsigned int df_rgx_is_valid_freq(unsigned long int freq) { unsigned int valid = 0; int i; int aSize = NUMBER_OF_LEVELS; if(!is_tng_a0) aSize = NUMBER_OF_LEVELS_B0; DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s freq: %d\n", __func__, freq); for(i = 0; i < aSize; i++) { if(freq == aAvailableStateFreq[i].freq) { valid = 1; break; } } DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s valid: %d\n", __func__, valid); return valid; }
/** * dev_freq_add_attributes_to_sysfs() - Creates all the sysfs entries * for the specified platform device. * @dev: platform device. */ int dev_freq_add_attributes_to_sysfs(struct device *device) { int error = 0; int i = 0; if (!device) return -1; for (i = 0; devfreq_attrs[i].attr.mode != 0; i++) { error = device_create_file(device, &devfreq_attrs[i]); if (error) { DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: could not" "create device file error = %0x\n", __func__, error); break; } } /*Were all the files created?*/ if (devfreq_attrs[i].attr.mode != 0) { int j = 0; for (j = 0; j < i; j++) device_remove_file(device, &devfreq_attrs[i]); } return error; }
/** * store_turbo_profile() - Write function for sysfs * sys/devices/platform/dfrgx/custom_turbo_profile. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. */ static ssize_t store_turbo_profile(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct busfreq_data *bfdata = dev_get_drvdata(dev); int ret = -EINVAL; int low_th = 0, high_th = 0; ret = sscanf(buf, "%d %d", &low_th, &high_th); DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: count = %zu, ret = %u\n", __func__, count, ret); if (ret != 2) goto out; if (low_th > high_th) { ret = 0; goto out; } a_governor_profile[DFRGX_TURBO_PROFILE_CUSTOM].util_th_low = low_th; a_governor_profile[DFRGX_TURBO_PROFILE_CUSTOM].util_th_high = high_th; bfdata->g_dfrgx_data.g_profile_index = DFRGX_TURBO_PROFILE_CUSTOM; ret = count; out: return ret; }
/** * df_rgx_bus_exit() - An optional callback that is called when devfreq is * removing the devfreq object due to error or from devfreq_remove_device() * call. If the user has registered devfreq->nb at a notifier-head, this is * the time to unregister it. */ static void df_rgx_bus_exit(struct device *dev) { struct busfreq_data *bfdata = dev_get_drvdata(dev); (void) bfdata; DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: entry\n", __func__); /* devfreq_unregister_opp_notifier(dev, bfdata->devfreq); */ }
/** * show_turbo_profile() - Read function for sysfs * sys/devices/platform/dfrgx/custom_turbo_profile. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. */ static ssize_t show_turbo_profile(struct device *dev, struct device_attribute *attr, char *buf) { DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n", __func__); return scnprintf(buf, PAGE_SIZE, "%d %d\n", a_governor_profile[DFRGX_TURBO_PROFILE_CUSTOM].util_th_low, a_governor_profile[DFRGX_TURBO_PROFILE_CUSTOM].util_th_high); }
/** * show_profiling_state() - Read function for sysfs * sys/devices/platform/dfrgx/profiling_state. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. */ static ssize_t show_profiling_state(struct device *dev, struct device_attribute *attr, char *buf) { struct busfreq_data *bfdata = dev_get_drvdata(dev); int profiling_state = dfrgx_profiling_is_enabled(&bfdata->g_dfrgx_data); DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: value = %d\n", __func__, profiling_state); return scnprintf(buf, PAGE_SIZE, "%d\n", profiling_state); }
/** * reset_profiling_stats() - Command function to reset stats. * Use cat sysfs sys/devices/platform/dfrgx/profiling_reset_stats * to perform this action. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. Ignored. */ static ssize_t reset_profiling_stats(struct device *dev, struct device_attribute *attr, char *buf) { DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n", __func__); gpu_profiling_records_restart(); return 0; }
/** * show_profiling_stats() - Read function for sysfs * sys/devices/platform/dfrgx/profiling_show_stats. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. */ static ssize_t show_profiling_stats(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n", __func__); ret = gpu_profiling_records_show(buf); return ret; }
/** * df_rgx_bus_get_dev_status() - Update current status, including: * - stat->current_frequency - Frequency in KHz. * - stat->total_time * - stat->busy_time * Note: total_time and busy_time have arbitrary units, as they are * used only as ratios. * Utilization is busy_time / total_time . */ static int df_rgx_bus_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct busfreq_data *bfdata = dev_get_drvdata(dev); DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: entry\n", __func__); stat->current_frequency = bfdata->bf_freq_mhz_rlzd * 1000; /* FIXME - Compute real utilization statistics. */ stat->total_time = 100; stat->busy_time = 50; return 0; }
/** * store_dynamic_turbo_state() - Write function for sysfs * sys/devices/platform/dfrgx/dynamic_turbo_state. * @dev: platform device. * @attr: 1 Attribute associated to this entry. * @buf: Buffer to write to. */ static ssize_t store_dynamic_turbo_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct busfreq_data *bfdata = dev_get_drvdata(dev); int dt_state; int ret = -EINVAL; ret = sscanf(buf, "%d", &dt_state); DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: count = %zu, ret = %u , state = %d\n", __func__, count, ret, dt_state); if (ret != 1) goto out; dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, dt_state); ret = count; out: return ret; }
/** * tcd_set_force_state_override() - thermal cooling device * callback set_force_state_override. * @tcd: Thermal cooling device structure. * @pcs: Pointer to char containing the input values. * * Invoked via interrupt/callback. * Function return value: 0 if success, otherwise -error. * Execution context: non-atomic */ static int tcd_set_force_state_override(struct thermal_cooling_device *tcd, char *buf) { struct busfreq_data *bfdata = (struct busfreq_data *) tcd->devdata; unsigned long int freqs[THERMAL_COOLING_DEVICE_MAX_STATE]; unsigned long int prev_freq = DFRGX_FREQ_533_MHZ; int i = 0; if (is_tng_a0) prev_freq = DFRGX_FREQ_320_MHZ; if (df_rgx_is_max_fuse_set()) prev_freq = DFRGX_FREQ_640_MHZ; sscanf(buf, "%lu %lu %lu %lu\n", &freqs[0], &freqs[1], &freqs[2], &freqs[3]); DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s values: %lu %lu %lu %lu\n", __func__, freqs[0], freqs[1], freqs[2], freqs[3]); for (i = 0; (i < THERMAL_COOLING_DEVICE_MAX_STATE) && df_rgx_is_valid_freq(freqs[i]) && prev_freq >= freqs[i]; i++) { prev_freq = freqs[i]; } if (i < THERMAL_COOLING_DEVICE_MAX_STATE) return -EINVAL; for (i = 0; i < THERMAL_COOLING_DEVICE_MAX_STATE; i++) bfdata->gpudata[i].freq_limit = freqs[i]; return 0; }
/** * tcd_set_cur_state() - thermal cooling * device callback set_cur_state. * @tcd: Thermal cooling device structure. * @cs: Input state. * * Invoked via interrupt/callback. * Function return value: 0 if success, otherwise -error. * Execution context: non-atomic */ static int tcd_set_cur_state(struct thermal_cooling_device *tcd, unsigned long cs) { struct busfreq_data *bfdata; struct devfreq *df; int ret = 0; bfdata = (struct busfreq_data *) tcd->devdata; if (cs >= THERMAL_COOLING_DEVICE_MAX_STATE) cs = THERMAL_COOLING_DEVICE_MAX_STATE - 1; /*If different state*/ if (bfdata->gbp_cooldv_state_cur != cs) { int new_index = -1; /* Dynamic turbo is not enabled so try * to change the state */ if (!bfdata->g_dfrgx_data.g_enable) { if(!df_rgx_is_active()) { return -EBUSY; } /* If thermal state is specified explicitely * then suspend burst/unburst thread * because the user needs the GPU to run * at specific frequency/thermal state level */ ret = df_rgx_set_freq_khz(bfdata, bfdata->gpudata[cs].freq_limit); if (ret <= 0) return ret; } else { /* In this case we want to limit the max_freq * to the thermal state limit */ int b_update_freq = 0; df = bfdata->devfreq; if (!cs) { /* We are back in normal operation so set initial values*/ df->max_freq = DF_RGX_FREQ_KHZ_MAX; df->min_freq = DF_RGX_FREQ_KHZ_MIN; b_update_freq = 1; } else { dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0); df->max_freq = bfdata->gpudata[cs].freq_limit; if (df->previous_freq > df->max_freq) b_update_freq = 1; if (bfdata->gpudata[cs].freq_limit < df->min_freq) { df->min_freq = bfdata->gpudata[cs].freq_limit; new_index = df_rgx_get_util_record_index_by_freq(df->min_freq); if (new_index > -1) { bfdata->g_dfrgx_data.g_freq_mhz_min = df->min_freq; bfdata->g_dfrgx_data.g_min_freq_index = new_index; } b_update_freq = 1; } new_index = df_rgx_get_util_record_index_by_freq(df->max_freq); if (new_index > -1) { bfdata->g_dfrgx_data.g_freq_mhz_max = df->max_freq; bfdata->g_dfrgx_data.g_max_freq_index = new_index; } dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 1); } if (b_update_freq) { /* Pick the min freq this time*/ bfdata->bf_desired_freq = df->min_freq; mutex_lock(&bfdata->lock); bfdata->b_need_freq_update = 1; mutex_unlock(&bfdata->lock); } } bfdata->gbp_cooldv_state_prev = bfdata->gbp_cooldv_state_cur; bfdata->gbp_cooldv_state_cur = cs; DFRGX_DPF(DFRGX_DEBUG_HIGH, "Thermal state changed from %d to %d\n", bfdata->gbp_cooldv_state_prev, bfdata->gbp_cooldv_state_cur); } return 0; }
/** * df_rgx_bus_target - Request setting of a new frequency. * @*p_freq: Input: desired frequency in KHz, output: realized freq in KHz. * @flags: DEVFREQ_FLAG_* - not used by this implementation. */ static int df_rgx_bus_target(struct device *dev, unsigned long *p_freq, u32 flags) { struct platform_device *pdev; struct busfreq_data *bfdata; struct df_rgx_data_s *pdfrgx_data; struct devfreq *df; unsigned long desired_freq = 0; int ret = 0; int adjust_curfreq = 0; int set_freq = 0; (void) flags; pdev = container_of(dev, struct platform_device, dev); bfdata = platform_get_drvdata(pdev); if (bfdata && bfdata->devfreq) { int gpu_defer_req = 0; df = bfdata->devfreq; pdfrgx_data = &bfdata->g_dfrgx_data; if (!pdfrgx_data || !pdfrgx_data->g_initialized) goto out; desired_freq = *p_freq; /* Governor changed, will be updated after updatedevfreq() */ if (strncmp(df->governor->name, bfdata->prev_governor, DEVFREQ_NAME_LEN)) { DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Governor changed," " prev : %s, current : %s!\n", __func__, bfdata->prev_governor, df->governor->name); if (dfrgx_burst_is_enabled(pdfrgx_data)) dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0); df_rgx_set_governor_profile(df->governor->name, pdfrgx_data); strncpy(bfdata->prev_governor, df->governor->name, DEVFREQ_NAME_LEN); DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Governors should be " "the same now, prev : %s, current : %s!\n", __func__, bfdata->prev_governor, df->governor->name); set_freq = 1; } else if (df->min_freq != pdfrgx_data->g_freq_mhz_min) { int new_index = -1; if (dfrgx_burst_is_enabled(pdfrgx_data)) dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0); new_index = df_rgx_get_util_record_index_by_freq(df->min_freq); if (new_index > -1) { mutex_lock(&pdfrgx_data->g_mutex_sts); pdfrgx_data->g_freq_mhz_min = df->min_freq; bfdata->gbp_cooldv_latest_freq_min = df->min_freq; pdfrgx_data->g_min_freq_index = new_index; mutex_unlock(&pdfrgx_data->g_mutex_sts); } DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:Min freq changed!," " prev_freq %lu, min_freq %lu\n", __func__, df->previous_freq, df->min_freq); if (df->previous_freq < df->min_freq) { desired_freq = df->min_freq; adjust_curfreq = 1; } } else if (df->max_freq != pdfrgx_data->g_freq_mhz_max) { int new_index = -1; if (dfrgx_burst_is_enabled(pdfrgx_data)) dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0); new_index = df_rgx_get_util_record_index_by_freq(df->max_freq); if (new_index > -1) { mutex_lock(&pdfrgx_data->g_mutex_sts); pdfrgx_data->g_freq_mhz_max = df->max_freq; bfdata->gbp_cooldv_latest_freq_max = df->max_freq; pdfrgx_data->g_max_freq_index = new_index; mutex_unlock(&pdfrgx_data->g_mutex_sts); } DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:Max freq changed!," " prev_freq %lu, max_freq %lu\n", __func__, df->previous_freq, df->max_freq); if (df->previous_freq > df->max_freq) { desired_freq = df->max_freq; adjust_curfreq = 1; } } else if (!strncmp(df->governor->name, "simple_ondemand", DEVFREQ_NAME_LEN)) { *p_freq = df->previous_freq; goto out; } /* set_freq changed on userspace governor*/ if (!strncmp(df->governor->name, "userspace", DEVFREQ_NAME_LEN)) { /* update userspace freq*/ struct userspace_gov_data *data = df->data; DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:userspace governor," " desired %lu, data->user_frequency %lu, input_freq = %lu\n", __func__, desired_freq, data->user_frequency, *p_freq); data->valid = 1; data->user_frequency = desired_freq; set_freq = 1; } if (adjust_curfreq) set_freq = 1; if (set_freq) { /* Freq will be reflected once GPU is back on*/ if (!df_rgx_is_active()) { bfdata->bf_desired_freq = desired_freq; mutex_lock(&bfdata->lock); bfdata->b_need_freq_update = 1; mutex_unlock(&bfdata->lock); *p_freq = desired_freq; gpu_defer_req = 1; } else { ret = df_rgx_set_freq_khz(bfdata, desired_freq); if (ret > 0) { *p_freq = ret; ret = 0; } } } else { *p_freq = df->previous_freq; } if ((!strncmp(df->governor->name, "simple_ondemand", DEVFREQ_NAME_LEN) && !dfrgx_burst_is_enabled(&bfdata->g_dfrgx_data)) || gpu_defer_req) dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 1); } out: return ret; }