Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d5f1005 authored by Lucille Sylvester's avatar Lucille Sylvester Committed by Gerrit - the friendly Code Review server
Browse files

msm: kgsl: Add a framework to track timing of power events



Allow histories of events of interest to power control
to be tracked.  Set up separate indexed arrays of each
type of desired event.  Power states and frequency changes
are two examples of tracked events.

Change-Id: I7953b9a34f797591e559a84cfd148307a4e9a4a5
Signed-off-by: default avatarLucille Sylvester <lsylvest@codeaurora.org>
parent 2b860e5c
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -110,6 +110,42 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
				unsigned int state);

/**
 * _record_pwrevent() - Record the history of the new event
 * @device: Pointer to the kgsl_device struct
 * @t: Timestamp
 * @event: Event type
 *
 * Finish recording the duration of the previous event.  Then update the
 * index, record the start of the new event, and the relevant data.
 */
static void _record_pwrevent(struct kgsl_device *device,
			ktime_t t, int event) {
	struct kgsl_pwrscale *psc = &device->pwrscale;
	struct kgsl_pwr_history *history = &psc->history[event];
	int i = history->index;
	if (history->events == NULL)
		return;
	history->events[i].duration = ktime_us_delta(t,
					history->events[i].start);
	i = (i + 1) % history->size;
	history->index = i;
	history->events[i].start = t;
	switch (event) {
	case KGSL_PWREVENT_STATE:
		history->events[i].data = device->state;
		break;
	case KGSL_PWREVENT_GPU_FREQ:
		history->events[i].data = device->pwrctrl.active_pwrlevel;
		break;
	case KGSL_PWREVENT_BUS_FREQ:
		history->events[i].data = last_vote_buslevel;
		break;
	default:
		break;
	}
}

/**
 * kgsl_get_bw() - Return latest msm bus IB vote
 */
@@ -2046,6 +2082,12 @@ int kgsl_pwrctrl_change_state(struct kgsl_device *device, int state)
		status = -EINVAL;
		break;
	}

	/* Record the state timing info */
	if (!status) {
		ktime_t t = ktime_get();
		_record_pwrevent(device, t, KGSL_PWREVENT_STATE);
	}
	return status;
}
EXPORT_SYMBOL(kgsl_pwrctrl_change_state);
+12 −1
Original line number Diff line number Diff line
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -610,6 +610,14 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor)
	pwrscale->next_governor_call = ktime_add_us(ktime_get(),
			KGSL_GOVERNOR_CALL_INTERVAL);

	/* history tracking */
	for (i = 0; i < KGSL_PWREVENT_MAX; i++) {
		pwrscale->history[i].events = kzalloc(
				pwrscale->history[i].size *
				sizeof(struct kgsl_pwr_event), GFP_KERNEL);
		pwrscale->history[i].type = i;
	}

	return 0;
}
EXPORT_SYMBOL(kgsl_pwrscale_init);
@@ -622,6 +630,7 @@ EXPORT_SYMBOL(kgsl_pwrscale_init);
 */
void kgsl_pwrscale_close(struct kgsl_device *device)
{
	int i;
	struct kgsl_pwrscale *pwrscale;

	BUG_ON(!mutex_is_locked(&device->mutex));
@@ -634,6 +643,8 @@ void kgsl_pwrscale_close(struct kgsl_device *device)
	devfreq_remove_device(device->pwrscale.devfreqptr);
	device->pwrscale.devfreqptr = NULL;
	srcu_cleanup_notifier_head(&device->pwrscale.nh);
	for (i = 0; i < KGSL_PWREVENT_MAX; i++)
		kfree(pwrscale->history[i].events);
}
EXPORT_SYMBOL(kgsl_pwrscale_close);

+28 −2
Original line number Diff line number Diff line
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -20,12 +20,32 @@
/* devfreq governor call window in usec */
#define KGSL_GOVERNOR_CALL_INTERVAL 10000

/* Power events to be tracked with history */
#define KGSL_PWREVENT_STATE	0
#define KGSL_PWREVENT_GPU_FREQ	1
#define KGSL_PWREVENT_BUS_FREQ	2
#define KGSL_PWREVENT_POPP	3
#define KGSL_PWREVENT_MAX	4

struct kgsl_power_stats {
	u64 busy_time;
	u64 ram_time;
	u64 ram_wait;
};

struct kgsl_pwr_event {
	unsigned int data;
	ktime_t start;
	s64 duration;
};

struct kgsl_pwr_history {
	struct kgsl_pwr_event *events;
	unsigned int type;
	unsigned int index;
	unsigned int size;
};

struct kgsl_pwrscale {
	struct devfreq *devfreqptr;
	struct msm_adreno_extended_profile gpu_profile;
@@ -42,6 +62,7 @@ struct kgsl_pwrscale {
	struct work_struct devfreq_resume_ws;
	struct work_struct devfreq_notify_ws;
	ktime_t next_governor_call;
	struct kgsl_pwr_history history[KGSL_PWREVENT_MAX];
};

int kgsl_pwrscale_init(struct device *dev, const char *governor);
@@ -80,5 +101,10 @@ int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq);
			.target = kgsl_busmon_target, \
			.get_dev_status = kgsl_busmon_get_dev_status, \
			.get_cur_freq = kgsl_busmon_get_cur_freq, \
	} } }
	} }, \
	.history[KGSL_PWREVENT_STATE].size = 10, \
	.history[KGSL_PWREVENT_GPU_FREQ].size = 3, \
	.history[KGSL_PWREVENT_BUS_FREQ].size = 5, \
	.history[KGSL_PWREVENT_POPP].size = 5, \
	}
#endif