diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 101 | 
1 files changed, 101 insertions, 0 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c44e562bdfe..bf6de38190c 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev)  	return ERR_PTR(-ENODEV);  } +/** + * devfreq_get_freq_level() - Lookup freq_table for the frequency + * @devfreq:	the devfreq instance + * @freq:	the target frequency + */ +static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) +{ +	int lev; + +	for (lev = 0; lev < devfreq->profile->max_state; lev++) +		if (freq == devfreq->profile->freq_table[lev]) +			return lev; + +	return -EINVAL; +} + +/** + * devfreq_update_status() - Update statistics of devfreq behavior + * @devfreq:	the devfreq instance + * @freq:	the update target frequency + */ +static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) +{ +	int lev, prev_lev; +	unsigned long cur_time; + +	lev = devfreq_get_freq_level(devfreq, freq); +	if (lev < 0) +		return lev; + +	cur_time = jiffies; +	devfreq->time_in_state[lev] += +			 cur_time - devfreq->last_stat_updated; +	if (freq != devfreq->previous_freq) { +		prev_lev = devfreq_get_freq_level(devfreq, +						devfreq->previous_freq); +		devfreq->trans_table[(prev_lev * +				devfreq->profile->max_state) + lev]++; +		devfreq->total_trans++; +	} +	devfreq->last_stat_updated = cur_time; + +	return 0; +} +  /* Load monitoring helper functions for governors use */  /** @@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq)  	if (err)  		return err; +	if (devfreq->profile->freq_table) +		if (devfreq_update_status(devfreq, freq)) +			dev_err(&devfreq->dev, +				"Couldn't update frequency transition information.\n"); +  	devfreq->previous_freq = freq;  	return err;  } @@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev,  	devfreq->data = data;  	devfreq->nb.notifier_call = devfreq_notifier_call; +	devfreq->trans_table =	devm_kzalloc(dev, sizeof(unsigned int) * +						devfreq->profile->max_state * +						devfreq->profile->max_state, +						GFP_KERNEL); +	devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * +						devfreq->profile->max_state, +						GFP_KERNEL); +	devfreq->last_stat_updated = jiffies; +  	dev_set_name(&devfreq->dev, dev_name(dev));  	err = device_register(&devfreq->dev);  	if (err) { @@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d,  	return count;  } +static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr, +				char *buf) +{ +	struct devfreq *devfreq = to_devfreq(dev); +	ssize_t len; +	int i, j, err; +	unsigned int max_state = devfreq->profile->max_state; + +	err = devfreq_update_status(devfreq, devfreq->previous_freq); +	if (err) +		return 0; + +	len = sprintf(buf, "   From  :   To\n"); +	len += sprintf(buf + len, "         :"); +	for (i = 0; i < max_state; i++) +		len += sprintf(buf + len, "%8u", +				devfreq->profile->freq_table[i]); + +	len += sprintf(buf + len, "   time(ms)\n"); + +	for (i = 0; i < max_state; i++) { +		if (devfreq->profile->freq_table[i] +					== devfreq->previous_freq) { +			len += sprintf(buf + len, "*"); +		} else { +			len += sprintf(buf + len, " "); +		} +		len += sprintf(buf + len, "%8u:", +				devfreq->profile->freq_table[i]); +		for (j = 0; j < max_state; j++) +			len += sprintf(buf + len, "%8u", +				devfreq->trans_table[(i * max_state) + j]); +		len += sprintf(buf + len, "%10u\n", +			jiffies_to_msecs(devfreq->time_in_state[i])); +	} + +	len += sprintf(buf + len, "Total transition : %u\n", +					devfreq->total_trans); +	return len; +} +  static struct device_attribute devfreq_attrs[] = {  	__ATTR(governor, S_IRUGO, show_governor, NULL),  	__ATTR(cur_freq, S_IRUGO, show_freq, NULL), @@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = {  	       store_polling_interval),  	__ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq),  	__ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), +	__ATTR(trans_stat, S_IRUGO, show_trans_table, NULL),  	{ },  };  |