static void nvhost_scale_notify(struct platform_device *pdev, bool busy) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_device_profile *profile = pdata->power_profile; struct devfreq *devfreq = pdata->power_manager; /* Is the device profile initialised? */ if (!profile) return; /* inform edp about new constraint */ if (pdata->gpu_edp_device) { u32 avg = 0; actmon_op().read_avg_norm(profile->actmon, &avg); BUG(); /* the next line passes a bogus frequency */ tegra_edp_notify_gpu_load(avg, 0); } /* If defreq is disabled, set the freq to max or min */ if (!devfreq) { unsigned long freq = busy ? UINT_MAX : 0; nvhost_scale_target(&pdev->dev, &freq, 0); return; } mutex_lock(&devfreq->lock); if (!profile->actmon) update_load_estimate(profile, busy); profile->dev_stat.busy = busy; update_devfreq(devfreq); mutex_unlock(&devfreq->lock); }
static int actmon_k_show(struct seq_file *s, void *unused) { struct nvhost_master *host = s->private; long period = actmon_op().get_k(host); seq_printf(s, "%ld\n", period); return 0; }
void nvhost_scale_hw_deinit(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_device_profile *profile = pdata->power_profile; if (profile && profile->actmon) actmon_op().deinit(profile->actmon); }
static int actmon_avg_norm_show(struct seq_file *s, void *unused) { struct nvhost_master *host = s->private; u32 avg; int err; err = actmon_op().read_avg_norm(host, &avg); if (!err) seq_printf(s, "%d\n", avg); return err; }
static ssize_t nvhost_scale_load_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nvhost_device_data *pdata = dev_get_drvdata(dev); struct nvhost_device_profile *profile = pdata->power_profile; u32 busy_time; ssize_t res; actmon_op().read_avg_norm(profile->actmon, &busy_time); res = snprintf(buf, PAGE_SIZE, "%u\n", busy_time); return res; }
static void update_load_estimate_actmon(struct nvhost_device_profile *profile) { ktime_t t; unsigned long dt; u32 busy_time; t = ktime_get(); dt = ktime_us_delta(t, profile->last_event_time); profile->dev_stat.total_time = dt; profile->last_event_time = t; actmon_op().read_avg_norm(profile->actmon, &busy_time); profile->dev_stat.busy_time = (busy_time * dt) / 1000; }
void nvhost_scale3d_callback(struct nvhost_device_profile *profile, unsigned long freq) { struct nvhost_gr3d_params *gr3d_params = profile->private_data; struct nvhost_device_data *pdata = platform_get_drvdata(profile->pdev); struct nvhost_emc_params *emc_params = &gr3d_params->emc_params; long hz; long after; /* Set EMC clockrate */ after = (long) clk_get_rate(clk(profile, gr3d_params->clk_3d)); hz = nvhost_scale3d_get_emc_rate(emc_params, after); nvhost_module_set_devfreq_rate(profile->pdev, gr3d_params->clk_3d_emc, hz); if (pdata->gpu_edp_device) { u32 avg = 0; actmon_op().read_avg_norm(profile->actmon, &avg); tegra_edp_notify_gpu_load(avg); } }
static int actmon_k_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct nvhost_master *host = s->private; char buffer[40]; int buf_size; unsigned long k; memset(buffer, 0, sizeof(buffer)); buf_size = min(count, (sizeof(buffer)-1)); if (copy_from_user(buffer, user_buf, buf_size)) return -EFAULT; if (kstrtoul(buffer, 10, &k)) return -EINVAL; actmon_op().set_k(host, k); return count; }
static int nvhost_scale3d_get_dev_status(struct device *d, struct devfreq_dev_status *stat) { struct platform_device *dev = to_platform_device(d); struct nvhost_master *host_master = nvhost_get_host(dev); struct nvhost_devfreq_ext_stat *ext_stat = power_profile.dev_stat->private_data; u32 avg = 0; ktime_t t; /* Make sure there are correct values for the current frequency */ power_profile.dev_stat->current_frequency = clk_get_rate(power_profile.clk_3d); /* Copy the contents of the current device status */ ext_stat->busy = power_profile.last_event_type; *stat = *power_profile.dev_stat; /* Read and scale AVG. AVG is scaled to interval 0-dt, where dt * is the last time it was read. (this is really clumsy, but the * governor uses internally time differences) */ actmon_op().read_avg_norm(host_master, &avg); t = ktime_get(); stat->total_time = ktime_us_delta(t, power_profile.last_request_time); stat->busy_time = (avg * stat->total_time) / 1000; power_profile.last_request_time = t; /* Finally, clear out the local values */ power_profile.dev_stat->total_time = 0; power_profile.dev_stat->busy_time = 0; power_profile.last_event_type = DEVICE_UNKNOWN; tegra_edp_notify_gpu_load(avg); return 0; }
static int actmon_above_wmark_show(struct seq_file *s, void *unused) { struct nvhost_master *host = s->private; seq_printf(s, "%d\n", actmon_op().above_wmark_count(host)); return 0; }
void nvhost_scale_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_device_profile *profile; int err; if (pdata->power_profile) return; profile = kzalloc(sizeof(struct nvhost_device_profile), GFP_KERNEL); if (!profile) return; pdata->power_profile = profile; profile->pdev = pdev; profile->clk = pdata->clk[0]; profile->dev_stat.busy = false; /* Create frequency table */ err = nvhost_scale_make_freq_table(profile); if (err || !profile->devfreq_profile.max_state) goto err_get_freqs; /* Initialize actmon */ if (pdata->actmon_enabled) { if (device_create_file(&pdev->dev, &dev_attr_load)) goto err_create_sysfs_entry; profile->actmon = kzalloc(sizeof(struct host1x_actmon), GFP_KERNEL); if (!profile->actmon) goto err_allocate_actmon; profile->actmon->host = nvhost_get_host(pdev); profile->actmon->regs = nvhost_get_host(pdev)->aperture + pdata->actmon_regs; actmon_op().init(profile->actmon); actmon_op().debug_init(profile->actmon, pdata->debugfs); actmon_op().deinit(profile->actmon); } if (pdata->devfreq_governor) { struct devfreq *devfreq; profile->devfreq_profile.initial_freq = profile->devfreq_profile.freq_table[0]; profile->devfreq_profile.target = nvhost_scale_target; profile->devfreq_profile.get_dev_status = nvhost_scale_get_dev_status; devfreq = devfreq_add_device(&pdev->dev, &profile->devfreq_profile, pdata->devfreq_governor, NULL); if (IS_ERR(devfreq)) devfreq = NULL; pdata->power_manager = devfreq; } /* Should we register QoS callback for this device? */ if (pdata->qos_id < PM_QOS_NUM_CLASSES && pdata->qos_id != PM_QOS_RESERVED) { profile->qos_notify_block.notifier_call = &nvhost_scale_qos_notify; pm_qos_add_notifier(pdata->qos_id, &profile->qos_notify_block); } return; err_get_freqs: err_allocate_actmon: device_remove_file(&pdev->dev, &dev_attr_load); err_create_sysfs_entry: kfree(pdata->power_profile); pdata->power_profile = NULL; }
void nvhost_scale_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_device_profile *profile; if (pdata->power_profile) return; profile = kzalloc(sizeof(struct nvhost_device_profile), GFP_KERNEL); if (!profile) return; pdata->power_profile = profile; profile->pdev = pdev; profile->clk = pdata->clk[0]; profile->last_event_type = DEVICE_UNKNOWN; /* Initialize devfreq related structures */ profile->dev_stat.private_data = &profile->ext_stat; profile->ext_stat.min_freq = clk_round_rate(clk_get_parent(profile->clk), 0); profile->ext_stat.max_freq = clk_round_rate(clk_get_parent(profile->clk), UINT_MAX); profile->ext_stat.busy = DEVICE_UNKNOWN; if (profile->ext_stat.min_freq == profile->ext_stat.max_freq) { dev_warn(&pdev->dev, "max rate = min rate (%lu), disabling scaling\n", profile->ext_stat.min_freq); goto err_fetch_clocks; } /* Initialize actmon */ if (pdata->actmon_enabled) { if (device_create_file(&pdev->dev, &dev_attr_load)) goto err_create_sysfs_entry; profile->actmon = kzalloc(sizeof(struct host1x_actmon), GFP_KERNEL); if (!profile->actmon) goto err_allocate_actmon; profile->actmon->host = nvhost_get_host(pdev); profile->actmon->regs = nvhost_get_host(pdev)->aperture + pdata->actmon_regs; actmon_op().init(profile->actmon); actmon_op().debug_init(profile->actmon, pdata->debugfs); actmon_op().deinit(profile->actmon); } if (pdata->devfreq_governor) { struct devfreq *devfreq = devfreq_add_device(&pdev->dev, &nvhost_scale_devfreq_profile, pdata->devfreq_governor, NULL); if (IS_ERR(devfreq)) devfreq = NULL; pdata->power_manager = devfreq; } return; err_allocate_actmon: device_remove_file(&pdev->dev, &dev_attr_load); err_create_sysfs_entry: err_fetch_clocks: kfree(pdata->power_profile); pdata->power_profile = NULL; }