int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation) { unsigned long flags; struct exynos_context *platform; KBASE_DEBUG_ASSERT(kbdev != NULL); platform = (struct exynos_context *) kbdev->platform_context; spin_lock_irqsave(&mali_dvfs_spinlock, flags); if (platform->time_tick < MALI_DVFS_TIME_INTERVAL) { platform->time_tick++; platform->time_busy += kbdev->pm.metrics.time_busy; platform->time_idle += kbdev->pm.metrics.time_idle; } else { platform->time_busy = kbdev->pm.metrics.time_busy; platform->time_idle = kbdev->pm.metrics.time_idle; platform->time_tick = 0; } if ((platform->time_tick == MALI_DVFS_TIME_INTERVAL) && (platform->time_idle + platform->time_busy > 0)) platform->utilisation = (100*platform->time_busy) / (platform->time_idle + platform->time_busy); mali_dvfs_status_current.utilisation = utilisation; #ifdef MALI_DEBUG printk(KERN_INFO "\n[mali_devfreq]utilization: %d\n", utilisation); #endif spin_unlock_irqrestore(&mali_dvfs_spinlock, flags); queue_work_on(0, mali_dvfs_wq, &mali_dvfs_work); /*add error handle here*/ return MALI_TRUE; }
void kbase_pm_register_vsync_callback(struct kbase_device *kbdev) { KBASE_DEBUG_ASSERT(kbdev != NULL); /* no VSync metrics will be available */ kbdev->pm.metrics.platform_data = NULL; }
void kbase_timeline_job_slot_done(kbase_device *kbdev, kbase_context *kctx, kbase_jd_atom *katom, int js, kbasep_js_atom_done_code done_code) { lockdep_assert_held(&kbdev->js_data.runpool_irq.lock); if (done_code & KBASE_JS_ATOM_DONE_EVICTED_FROM_NEXT) { KBASE_TIMELINE_JOB_START_NEXT(kctx, js, 0); } else { /* Job finished in JSn_HEAD */ base_atom_id atom_number = kbase_jd_atom_id(kctx, katom); KBASE_TIMELINE_JOB_START_HEAD(kctx, js, 0); KBASE_TIMELINE_JOB_STOP(kctx, js, atom_number); /* see if we need to trace the job in JSn_NEXT moving to JSn_HEAD */ if (kbdev->timeline.slot_atoms_submitted[js] > 1) { /* Tag events with next_katom's kctx */ kbase_jm_slot *slot = &kbdev->jm_slots[js]; kbase_jd_atom *next_katom; kbase_context *next_kctx; KBASE_DEBUG_ASSERT(kbasep_jm_nr_jobs_submitted(slot) > 0); /* Peek the next atom - note that the atom in JSn_HEAD will already * have been dequeued */ next_katom = kbasep_jm_peek_idx_submit_slot(slot, 0); next_kctx = next_katom->kctx; KBASE_TIMELINE_JOB_START_NEXT(next_kctx, js, 0); KBASE_TIMELINE_JOB_START_HEAD(next_kctx, js, 1); KBASE_TIMELINE_JOB_START(next_kctx, js, kbase_jd_atom_id(next_kctx, next_katom)); } } --kbdev->timeline.slot_atoms_submitted[js]; KBASE_TIMELINE_ATOMS_SUBMITTED(kctx, js, kbdev->timeline.slot_atoms_submitted[js]); }
const struct kbase_pm_ca_policy *kbase_pm_ca_get_policy(struct kbase_device *kbdev) { KBASE_DEBUG_ASSERT(kbdev != NULL); return kbdev->pm.backend.ca_current_policy; }
int kbasep_jd_debugfs_ctx_add(struct kbase_context *kctx) { /* Refer below for format string, %u is 10 chars max */ char dir_name[10 * 2 + 2]; KBASE_DEBUG_ASSERT(kctx != NULL); /* Create per-context directory */ scnprintf(dir_name, sizeof(dir_name), "%u_%u", kctx->pid, kctx->id); kctx->jd_ctx_dir = debugfs_create_dir(dir_name, kctx->kbdev->jd_directory); if (IS_ERR(kctx->jd_ctx_dir)) goto err; /* Expose all atoms */ if (IS_ERR(debugfs_create_file("atoms", S_IRUGO, kctx->jd_ctx_dir, kctx, &kbasep_jd_debugfs_atoms_fops))) goto err_jd_ctx_dir; return 0; err_jd_ctx_dir: debugfs_remove_recursive(kctx->jd_ctx_dir); err: return -1; }
/** * kbase_context_set_create_flags - Set creation flags on a context * @kctx: Kbase context * @flags: Flags to set * * Return: 0 on success */ int kbase_context_set_create_flags(struct kbase_context *kctx, u32 flags) { int err = 0; struct kbasep_js_kctx_info *js_kctx_info; unsigned long irq_flags; KBASE_DEBUG_ASSERT(NULL != kctx); js_kctx_info = &kctx->jctx.sched_info; /* Validate flags */ if (flags != (flags & BASE_CONTEXT_CREATE_KERNEL_FLAGS)) { err = -EINVAL; goto out; } mutex_lock(&js_kctx_info->ctx.jsctx_mutex); spin_lock_irqsave(&kctx->kbdev->js_data.runpool_irq.lock, irq_flags); /* Translate the flags */ if ((flags & BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED) == 0) js_kctx_info->ctx.flags &= ~((u32) KBASE_CTX_FLAG_SUBMIT_DISABLED); if ((flags & BASE_CONTEXT_HINT_ONLY_COMPUTE) != 0) js_kctx_info->ctx.flags |= (u32) KBASE_CTX_FLAG_HINT_ONLY_COMPUTE; /* Latch the initial attributes into the Job Scheduler */ kbasep_js_ctx_attr_set_initial_attrs(kctx->kbdev, kctx); spin_unlock_irqrestore(&kctx->kbdev->js_data.runpool_irq.lock, irq_flags); mutex_unlock(&js_kctx_info->ctx.jsctx_mutex); out: return err; }
/* Find region enclosing given address. */ struct kbase_va_region *kbase_region_tracker_find_region_enclosing_address(struct kbase_context *kctx, u64 gpu_addr) { struct rb_node *rbnode; struct kbase_va_region *reg; u64 gpu_pfn = gpu_addr >> PAGE_SHIFT; KBASE_DEBUG_ASSERT(NULL != kctx); lockdep_assert_held(&kctx->reg_lock); rbnode = kctx->reg_rbtree.rb_node; while (rbnode) { u64 tmp_start_pfn, tmp_end_pfn; reg = rb_entry(rbnode, struct kbase_va_region, rblink); tmp_start_pfn = reg->start_pfn; tmp_end_pfn = reg->start_pfn + reg->nr_pages; /* If start is lower than this, go left. */ if (gpu_pfn < tmp_start_pfn) rbnode = rbnode->rb_left; /* If end is higher than this, then go right. */ else if (gpu_pfn >= tmp_end_pfn) rbnode = rbnode->rb_right; else /* Enclosing */ return reg; } return NULL; }
void kbasep_jd_debugfs_ctx_remove(struct kbase_context *kctx) { KBASE_DEBUG_ASSERT(kctx != NULL); if (!IS_ERR(kctx->jd_ctx_dir)) debugfs_remove_recursive(kctx->jd_ctx_dir); }
/** Show callback for the @c mem_profile debugfs file. * * This function is called to get the contents of the @c mem_profile debugfs * file. This is a report of current memory usage and distribution in userspace. * * @param sfile The debugfs entry * @param data Data associated with the entry * * @return 0 if successfully prints data in debugfs entry file * -1 if it encountered an error */ static int kbasep_mem_profile_seq_show(struct seq_file *sfile, void *data) { struct kbase_context *kctx = sfile->private; KBASE_DEBUG_ASSERT(kctx != NULL); /* MALI_SEC_INTEGRATION */ { struct kbase_device *kbdev = kctx->kbdev; atomic_inc(&kctx->mem_profile_showing_state); if(kbdev->vendor_callbacks->mem_profile_check_kctx) if (!kbdev->vendor_callbacks->mem_profile_check_kctx(kctx)) { atomic_dec(&kctx->mem_profile_showing_state); return 0; } } /* MALI_SEC_INTEGRATION */ if (kctx->destroying_context) { atomic_dec(&kctx->mem_profile_showing_state); return 0; } spin_lock(&kctx->mem_profile_lock); /* MALI_SEC_INTEGRATION */ if (kctx->mem_profile_data) { seq_write(sfile, kctx->mem_profile_data, kctx->mem_profile_size); seq_putc(sfile, '\n'); } spin_unlock(&kctx->mem_profile_lock); atomic_dec(&kctx->mem_profile_showing_state); return 0; }
/** * Workqueue for handling cache cleaning */ void kbasep_cache_clean_worker(struct work_struct *data) { struct kbase_device *kbdev; unsigned long flags; kbdev = container_of(data, struct kbase_device, hwcnt.cache_clean_work); mutex_lock(&kbdev->cacheclean_lock); kbasep_instr_hwcnt_cacheclean(kbdev); spin_lock_irqsave(&kbdev->hwcnt.lock, flags); /* Wait for our condition, and any reset to complete */ while (kbdev->hwcnt.state == KBASE_INSTR_STATE_RESETTING || kbdev->hwcnt.state == KBASE_INSTR_STATE_CLEANING) { spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags); wait_event(kbdev->hwcnt.cache_clean_wait, (kbdev->hwcnt.state != KBASE_INSTR_STATE_RESETTING && kbdev->hwcnt.state != KBASE_INSTR_STATE_CLEANING)); spin_lock_irqsave(&kbdev->hwcnt.lock, flags); } KBASE_DEBUG_ASSERT(kbdev->hwcnt.state == KBASE_INSTR_STATE_CLEANED); /* All finished and idle */ kbdev->hwcnt.state = KBASE_INSTR_STATE_IDLE; kbdev->hwcnt.triggered = 1; wake_up(&kbdev->hwcnt.wait); spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags); mutex_unlock(&kbdev->cacheclean_lock); }
void kbase_gpuprops_set(kbase_device *kbdev) { kbase_gpu_props *gpu_props; struct midg_raw_gpu_props *raw; KBASE_DEBUG_ASSERT(NULL != kbdev); gpu_props = &kbdev->gpu_props; raw = &gpu_props->props.raw_props; /* Initialize the base_gpu_props structure from the hardware */ kbase_gpuprops_get_props(&gpu_props->props, kbdev); /* Populate the derived properties */ kbase_gpuprops_calculate_props(&gpu_props->props, kbdev); /* Populate kbase-only fields */ gpu_props->l2_props.associativity = KBASE_UBFX32(raw->l2_features, 8U, 8); gpu_props->l2_props.external_bus_width = KBASE_UBFX32(raw->l2_features, 24U, 8); gpu_props->l3_props.associativity = KBASE_UBFX32(raw->l3_features, 8U, 8); gpu_props->l3_props.external_bus_width = KBASE_UBFX32(raw->l3_features, 24U, 8); gpu_props->mem.core_group = KBASE_UBFX32(raw->mem_features, 0U, 1); gpu_props->mem.supergroup = KBASE_UBFX32(raw->mem_features, 1U, 1); gpu_props->mmu.va_bits = KBASE_UBFX32(raw->mmu_features, 0U, 8); gpu_props->mmu.pa_bits = KBASE_UBFX32(raw->mmu_features, 8U, 8); gpu_props->num_cores = hweight64(raw->shader_present); gpu_props->num_core_groups = hweight64(raw->l2_present); gpu_props->num_supergroups = hweight64(raw->l3_present); gpu_props->num_address_spaces = hweight32(raw->as_present); gpu_props->num_job_slots = hweight32(raw->js_present); }
STATIC INLINE mali_bool kbase_js_affinity_is_violating(struct kbase_device *kbdev, u64 *affinities) { /* This implementation checks whether the two slots involved in Generic thread creation * have intersecting affinity. This is due to micro-architectural issues where a job in * slot A targetting cores used by slot B could prevent the job in slot B from making * progress until the job in slot A has completed. * * @note It just so happens that this restriction also allows * BASE_HW_ISSUE_8987 to be worked around by placing on job slot 2 the * atoms from ctxs with KBASE_CTX_FLAG_HINT_ONLY_COMPUTE flag set */ u64 affinity_set_left; u64 affinity_set_right; u64 intersection; KBASE_DEBUG_ASSERT(affinities != NULL); affinity_set_left = affinities[1]; if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8987)) { /* The left set also includes those on the Fragment slot when * we are using the HW workaround for BASE_HW_ISSUE_8987 */ affinity_set_left |= affinities[0]; } affinity_set_right = affinities[2]; /* A violation occurs when any bit in the left_set is also in the right_set */ intersection = affinity_set_left & affinity_set_right; return (mali_bool) (intersection != (u64) 0u); }
/* This function inserts a region into the tree. */ static void kbase_region_tracker_insert(struct kbase_context *kctx, struct kbase_va_region *new_reg) { u64 start_pfn = new_reg->start_pfn; struct rb_node **link = &(kctx->reg_rbtree.rb_node); struct rb_node *parent = NULL; /* Find the right place in the tree using tree search */ while (*link) { struct kbase_va_region *old_reg; parent = *link; old_reg = rb_entry(parent, struct kbase_va_region, rblink); /* RBTree requires no duplicate entries. */ KBASE_DEBUG_ASSERT(old_reg->start_pfn != start_pfn); if (old_reg->start_pfn > start_pfn) link = &(*link)->rb_left; else link = &(*link)->rb_right; } /* Put the new node there, and rebalance tree */ rb_link_node(&(new_reg->rblink), parent, link); rb_insert_color(&(new_reg->rblink), &(kctx->reg_rbtree)); }
void kbasep_jd_debugfs_term(struct kbase_device *kbdev) { KBASE_DEBUG_ASSERT(kbdev != NULL); if (!IS_ERR(kbdev->jd_directory)) debugfs_remove_recursive(kbdev->jd_directory); }
static inline bool kbase_js_affinity_is_violating( struct kbase_device *kbdev, u64 *affinities) { /* This implementation checks whether the two slots involved in Generic * thread creation have intersecting affinity. This is due to micro- * architectural issues where a job in slot A targetting cores used by * slot B could prevent the job in slot B from making progress until the * job in slot A has completed. */ u64 affinity_set_left; u64 affinity_set_right; u64 intersection; KBASE_DEBUG_ASSERT(affinities != NULL); affinity_set_left = affinities[1]; affinity_set_right = affinities[2]; /* A violation occurs when any bit in the left_set is also in the * right_set */ intersection = affinity_set_left & affinity_set_right; return (bool) (intersection != (u64) 0u); }
void kbase_pm_ca_set_policy(struct kbase_device *kbdev, const struct kbase_pm_ca_policy *new_policy) { const struct kbase_pm_ca_policy *old_policy; unsigned long flags; KBASE_DEBUG_ASSERT(kbdev != NULL); KBASE_DEBUG_ASSERT(new_policy != NULL); KBASE_TRACE_ADD(kbdev, PM_CA_SET_POLICY, NULL, NULL, 0u, new_policy->id); /* During a policy change we pretend the GPU is active */ /* A suspend won't happen here, because we're in a syscall from a userspace thread */ kbase_pm_context_active(kbdev); wake_lock(&kbdev->pm.kbase_wake_lock); mutex_lock(&kbdev->pm.lock); /* Remove the policy to prevent IRQ handlers from working on it */ spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); old_policy = kbdev->pm.ca_current_policy; kbdev->pm.ca_current_policy = NULL; spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); if (old_policy->term) old_policy->term(kbdev); if (new_policy->init) new_policy->init(kbdev); spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); kbdev->pm.ca_current_policy = new_policy; /* If any core power state changes were previously attempted, but couldn't * be made because the policy was changing (current_policy was NULL), then * re-try them here. */ kbase_pm_update_cores_state_nolock(kbdev); kbdev->pm.ca_current_policy->update_core_status(kbdev, kbdev->shader_ready_bitmap, kbdev->shader_transitioning_bitmap); spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); mutex_unlock(&kbdev->pm.lock); wake_unlock(&kbdev->pm.kbase_wake_lock); /* Now the policy change is finished, we release our fake context active reference */ kbase_pm_context_idle(kbdev); }
void kbase_pm_halt(kbase_device *kbdev) { KBASE_DEBUG_ASSERT(kbdev != NULL); mutex_lock(&kbdev->pm.lock); kbase_pm_do_poweroff(kbdev); mutex_unlock(&kbdev->pm.lock); }
bool kbase_js_affinity_would_violate(struct kbase_device *kbdev, int js, u64 affinity) { struct kbasep_js_device_data *js_devdata; u64 new_affinities[BASE_JM_MAX_NR_SLOTS]; KBASE_DEBUG_ASSERT(kbdev != NULL); KBASE_DEBUG_ASSERT(js < BASE_JM_MAX_NR_SLOTS); js_devdata = &kbdev->js_data; memcpy(new_affinities, js_devdata->runpool_irq.slot_affinities, sizeof(js_devdata->runpool_irq.slot_affinities)); new_affinities[js] |= affinity; return kbase_js_affinity_is_violating(kbdev, new_affinities); }
/** * @brief Show callback for the @c JD atoms debugfs file. * * This function is called to get the contents of the @c JD atoms debugfs file. * This is a report of all atoms managed by kbase_jd_context::atoms . * * @param sfile The debugfs entry * @param data Data associated with the entry * * @return 0 if successfully prints data in debugfs entry file, failure * otherwise */ static int kbasep_jd_debugfs_atoms_show(struct seq_file *sfile, void *data) { struct kbase_context *kctx = sfile->private; struct kbase_jd_atom *atoms; unsigned long irq_flags; int i; KBASE_DEBUG_ASSERT(kctx != NULL); /* Print table heading */ seq_puts(sfile, "atom id,core reqs,status,coreref status,predeps,start time,time on gpu\n"); atoms = kctx->jctx.atoms; /* General atom states */ mutex_lock(&kctx->jctx.lock); /* JS-related states */ spin_lock_irqsave(&kctx->kbdev->js_data.runpool_irq.lock, irq_flags); for (i = 0; i != BASE_JD_ATOM_COUNT; ++i) { struct kbase_jd_atom *atom = &atoms[i]; s64 start_timestamp = 0; if (atom->status == KBASE_JD_ATOM_STATE_UNUSED) continue; /* start_timestamp is cleared as soon as the atom leaves UNUSED state * and set before a job is submitted to the h/w, a non-zero value means * it is valid */ if (ktime_to_ns(atom->start_timestamp)) start_timestamp = ktime_to_ns( ktime_sub(ktime_get(), atom->start_timestamp)); /* MALI_SEC_INTEGRATION */ #ifdef CONFIG_ARM64 seq_printf(sfile, "%i,%u,%u,%u,%ld,%ld,%lli,%llu\n", i, atom->core_req, atom->status, atom->coreref_state, atom->dep[0].atom ? atom->dep[0].atom - atoms : 0, atom->dep[1].atom ? atom->dep[1].atom - atoms : 0, (signed long long)start_timestamp, (unsigned long long)(atom->time_spent_us ? atom->time_spent_us * 1000 : start_timestamp) ); #else seq_printf(sfile, "%i,%u,%u,%u,%d,%d,%lli,%llu\n", i, atom->core_req, atom->status, atom->coreref_state, atom->dep[0].atom ? atom->dep[0].atom - atoms : 0, atom->dep[1].atom ? atom->dep[1].atom - atoms : 0, (signed long long)start_timestamp, (unsigned long long)(atom->time_spent_us ? atom->time_spent_us * 1000 : start_timestamp) ); #endif } spin_unlock_irqrestore(&kctx->kbdev->js_data.runpool_irq.lock, irq_flags); mutex_unlock(&kctx->jctx.lock); return 0; }
int kbase_pm_context_active_handle_suspend(struct kbase_device *kbdev, enum kbase_pm_suspend_handler suspend_handler) { int c; int old_count; KBASE_DEBUG_ASSERT(kbdev != NULL); /* Trace timeline information about how long it took to handle the decision * to powerup. Sometimes the event might be missed due to reading the count * outside of mutex, but this is necessary to get the trace timing * correct. */ old_count = kbdev->pm.active_count; if (old_count == 0) kbase_timeline_pm_send_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); mutex_lock(&kbdev->pm.lock); if (kbase_pm_is_suspending(kbdev)) { switch (suspend_handler) { case KBASE_PM_SUSPEND_HANDLER_DONT_REACTIVATE: if (kbdev->pm.active_count != 0) break; /* FALLTHROUGH */ case KBASE_PM_SUSPEND_HANDLER_DONT_INCREASE: mutex_unlock(&kbdev->pm.lock); if (old_count == 0) kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); return 1; case KBASE_PM_SUSPEND_HANDLER_NOT_POSSIBLE: /* FALLTHROUGH */ default: KBASE_DEBUG_ASSERT_MSG(MALI_FALSE, "unreachable"); break; } } c = ++kbdev->pm.active_count; KBASE_TIMELINE_CONTEXT_ACTIVE(kbdev, c); KBASE_TRACE_ADD_REFCOUNT(kbdev, PM_CONTEXT_ACTIVE, NULL, NULL, 0u, c); /* Trace the event being handled */ if (old_count == 0) kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); if (c == 1) { /* First context active: Power on the GPU and any cores requested by * the policy */ kbase_pm_update_active(kbdev); #ifndef MALI_SEC_SEPERATED_UTILIZATION kbasep_pm_record_gpu_active(kbdev); #endif } mutex_unlock(&kbdev->pm.lock); return 0; }
void kbase_pm_suspend(struct kbase_device *kbdev) { int nr_keep_gpu_powered_ctxs; KBASE_DEBUG_ASSERT(kbdev); mutex_lock(&kbdev->pm.lock); KBASE_DEBUG_ASSERT(!kbase_pm_is_suspending(kbdev)); kbdev->pm.suspending = MALI_TRUE; mutex_unlock(&kbdev->pm.lock); /* From now on, the active count will drop towards zero. Sometimes, it'll * go up briefly before going down again. However, once it reaches zero it * will stay there - guaranteeing that we've idled all pm references */ /* Suspend job scheduler and associated components, so that it releases all * the PM active count references */ kbasep_js_suspend(kbdev); /* Suspend any counter collection that might be happening */ kbase_instr_hwcnt_suspend(kbdev); /* Cancel the keep_gpu_powered calls */ for (nr_keep_gpu_powered_ctxs = atomic_read(&kbdev->keep_gpu_powered_count); nr_keep_gpu_powered_ctxs > 0; --nr_keep_gpu_powered_ctxs) { kbase_pm_context_idle(kbdev); } /* Wait for the active count to reach zero. This is not the same as * waiting for a power down, since not all policies power down when this * reaches zero. */ wait_event(kbdev->pm.zero_active_count_wait, kbdev->pm.active_count == 0); /* NOTE: We synchronize with anything that was just finishing a * kbase_pm_context_idle() call by locking the pm.lock below */ /* Force power off the GPU and all cores (regardless of policy), only after * the PM active count reaches zero (otherwise, we risk turning it off * prematurely) */ mutex_lock(&kbdev->pm.lock); kbase_pm_cancel_deferred_poweroff(kbdev); kbase_pm_do_poweroff(kbdev, MALI_TRUE); mutex_unlock(&kbdev->pm.lock); }
/** * @brief Issue Dump command to hardware * * Notes: * - does not sleep */ mali_error kbase_instr_hwcnt_dump_irq(struct kbase_context *kctx) { unsigned long flags; mali_error err = MALI_ERROR_FUNCTION_FAILED; struct kbase_device *kbdev; KBASE_DEBUG_ASSERT(NULL != kctx); kbdev = kctx->kbdev; KBASE_DEBUG_ASSERT(NULL != kbdev); spin_lock_irqsave(&kbdev->hwcnt.lock, flags); if (kbdev->hwcnt.kctx != kctx) { /* The instrumentation has been setup for another context */ GPU_LOG(DVFS_INFO, DUMMY, 0u, 0u, "hwcnt irq error in %s %d \n", __FUNCTION__, err); goto unlock; } if (kbdev->hwcnt.state != KBASE_INSTR_STATE_IDLE) { /* HW counters are disabled or another dump is ongoing, or we're resetting */ GPU_LOG(DVFS_INFO, DUMMY, 0u, 0u, "hwcnt disabled or another dump is ongoing in %s %d \n", __FUNCTION__, err); goto unlock; } kbdev->hwcnt.triggered = 0; /* Mark that we're dumping - the PF handler can signal that we faulted */ kbdev->hwcnt.state = KBASE_INSTR_STATE_DUMPING; /* Reconfigure the dump address */ kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_LO), kbdev->hwcnt.addr & 0xFFFFFFFF, NULL); kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_HI), kbdev->hwcnt.addr >> 32, NULL); /* Start dumping */ KBASE_TRACE_ADD(kbdev, CORE_GPU_PRFCNT_SAMPLE, NULL, NULL, kbdev->hwcnt.addr, 0); kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND), GPU_COMMAND_PRFCNT_SAMPLE, kctx); dev_dbg(kbdev->dev, "HW counters dumping done for context %p", kctx); err = MALI_ERROR_NONE; unlock: spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags); return err; }
void kbasep_mem_profile_debugfs_add(struct kbase_context *kctx) { KBASE_DEBUG_ASSERT(kctx != NULL); spin_lock_init(&kctx->mem_profile_lock); debugfs_create_file("mem_profile", S_IRUGO, kctx->kctx_dentry, kctx, &kbasep_mem_profile_debugfs_fops); }
void kbase_pm_halt(struct kbase_device *kbdev) { KBASE_DEBUG_ASSERT(kbdev != NULL); mutex_lock(&kbdev->pm.lock); kbase_pm_cancel_deferred_poweroff(kbdev); kbase_pm_do_poweroff(kbdev, MALI_FALSE); mutex_unlock(&kbdev->pm.lock); }
void kbasep_jd_debugfs_ctx_add(struct kbase_context *kctx) { KBASE_DEBUG_ASSERT(kctx != NULL); /* Expose all atoms */ debugfs_create_file("atoms", S_IRUGO, kctx->kctx_dentry, kctx, &kbasep_jd_debugfs_atoms_fops); }
STATIC base_jd_udata kbase_event_process(kbase_context *kctx, kbase_jd_atom *katom) { base_jd_udata data; KBASE_DEBUG_ASSERT(kctx != NULL); KBASE_DEBUG_ASSERT(katom != NULL); KBASE_DEBUG_ASSERT(katom->status == KBASE_JD_ATOM_STATE_COMPLETED); data = katom->udata; KBASE_TIMELINE_ATOMS_IN_FLIGHT(kctx, atomic_sub_return(1, &kctx->timeline.jd_atoms_in_flight)); katom->status = KBASE_JD_ATOM_STATE_UNUSED; wake_up(&katom->completed); return data; }
void kbasep_mem_profile_debugfs_remove(struct kbase_context *kctx) { KBASE_DEBUG_ASSERT(kctx != NULL); spin_lock(&kctx->mem_profile_lock); kfree(kctx->mem_profile_data); kctx->mem_profile_data = NULL; spin_unlock(&kctx->mem_profile_lock); }
/* Disable instrumentation and wait for any existing dump to complete * It's assumed that there's only one privileged context * Safe to do this without lock when doing an OS suspend, because it only * changes in response to user-space IOCTLs */ void kbase_instr_hwcnt_suspend(kbase_device *kbdev) { kbase_context *kctx; KBASE_DEBUG_ASSERT(kbdev); KBASE_DEBUG_ASSERT(!kbdev->hwcnt.suspended_kctx); kctx = kbdev->hwcnt.kctx; kbdev->hwcnt.suspended_kctx = kctx; /* Relevant state was saved into hwcnt.suspended_state when enabling the * counters */ if (kctx) { KBASE_DEBUG_ASSERT(kctx->jctx.sched_info.ctx.flags & KBASE_CTX_FLAG_PRIVILEGED); kbase_instr_hwcnt_disable(kctx); } }
void kbase_pm_context_idle(struct kbase_device *kbdev) { int c; int old_count; KBASE_DEBUG_ASSERT(kbdev != NULL); /* Trace timeline information about how long it took to handle the decision * to powerdown. Sometimes the event might be missed due to reading the * count outside of mutex, but this is necessary to get the trace timing * correct. */ old_count = kbdev->pm.active_count; if (old_count == 0) kbase_timeline_pm_send_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_IDLE); mutex_lock(&kbdev->pm.lock); c = --kbdev->pm.active_count; KBASE_TIMELINE_CONTEXT_ACTIVE(kbdev, c); KBASE_TRACE_ADD_REFCOUNT(kbdev, PM_CONTEXT_IDLE, NULL, NULL, 0u, c); KBASE_DEBUG_ASSERT(c >= 0); /* Trace the event being handled */ if (old_count == 0) kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_IDLE); if (c == 0) { /* Last context has gone idle */ kbase_pm_update_active(kbdev); #ifndef MALI_SEC_SEPERATED_UTILIZATION kbasep_pm_record_gpu_idle(kbdev); #endif /* Wake up anyone waiting for this to become 0 (e.g. suspend). The * waiters must synchronize with us by locking the pm.lock after * waiting */ wake_up(&kbdev->pm.zero_active_count_wait); } mutex_unlock(&kbdev->pm.lock); }
static int gpu_get_clock(struct kbase_device *kbdev) { struct exynos_context *platform = (struct exynos_context *) kbdev->platform_context; if (!platform) return -ENODEV; KBASE_DEBUG_ASSERT(kbdev != NULL); fin_pll = clk_get(kbdev->dev, "fin_pll"); if (IS_ERR(fin_pll) || (fin_pll == NULL)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [fin_pll]\n", __func__); return -1; } fout_g3d_pll = clk_get(kbdev->dev, "fout_g3d_pll"); if (IS_ERR(fout_g3d_pll)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [fout_g3d_pll]\n", __func__); return -1; } aclk_g3d = clk_get(kbdev->dev, "aclk_g3d"); if (IS_ERR(aclk_g3d)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [aclk_g3d]\n", __func__); return -1; } mout_g3d = clk_get(kbdev->dev, "mout_g3d"); if (IS_ERR(mout_g3d)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [mout_g3d]\n", __func__); return -1; } sclk_hpm_g3d = clk_get(kbdev->dev, "sclk_hpm_g3d"); if (IS_ERR(sclk_hpm_g3d)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [sclk_hpm_g3d]\n", __func__); return -1; } aclk_lh_g3d0 = clk_get(kbdev->dev, "aclk_lh_g3d0"); if (IS_ERR(aclk_lh_g3d0)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [aclk_lh_g3d0]\n", __func__); return -1; } aclk_lh_g3d1 = clk_get(kbdev->dev, "aclk_lh_g3d1"); if (IS_ERR(aclk_lh_g3d1)) { GPU_LOG(DVFS_ERROR, DUMMY, 0u, 0u, "%s: failed to clk_get [aclk_lh_g3d1]\n", __func__); return -1; } __raw_writel(0x1, EXYNOS7420_MUX_SEL_G3D); gpu_enable_clock(platform); return 0; }