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;
}
Example #7
0
/* 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);
}
Example #13
0
/* 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;
}
Example #21
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);
}
Example #22
0
/**
 * @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);
}
Example #24
0
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;
}