Re: Dynamic cpufreq_frequency_table?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Mar 6, 2013 at 10:25 AM, Guru Prasad <gurupras@xxxxxxxxxxx> wrote:
> Hi Viresh,
>
> I think I found the problem in my code.
> I made it a point to explicitly add CPUFREQ_TABLE_END as an entry in
> my cpufreq_freq_table, but while calculating the steps between
> min_freq and max_freq, I failed to allocate an extra step for the
> CPUFREQ_TABLE_END entry.
> This probably caused some segfault in the driver and it failed to init.
>
> Please find my code attached.
> Since I'm not well versed in writing kernel code, I would appreciate
> it if you could take a quick glace and pointed out the flaws
>
> I have followed used mach-integrator as a base to write this code.
>
> Thank you for your help.
>
> Regards
> Guru
>
>
> On Tue, Mar 5, 2013 at 7:31 PM, Viresh Kumar <viresh.kumar@xxxxxxxxxx> wrote:
>> On 6 March 2013 01:28, Guru Prasad <gurupras@xxxxxxxxxxx> wrote:
>>> What I mean is, every time i start the simulation with a new clock
>>> frequency, I have my cpufreq_init function do the following
>>>         int steps;
>>>         policy->cpuinfo.max_freq = my_cpufreq_get(policy->cpu); //In KHz
>>>         policy->cpuinfo.min_freq = 100  * 1000; //In KHz
>>
>> You don't have to set these, but you just need to pass the right table
>> to cpufreq core and call cpufreq_frequency_table_cpuinfo(). These would
>> be set accordingly by this routine.
>>
>>>         policy->cpuinfo.transition_latency = 10 * 1000; //In us
>>>         policy->cur = my_cpufreq_get(policy->cpu);      //(I'm assuming that
>>> the CPU frequency is always at the maximum before the driver takes
>>> control.)
>>>
>>>
>>> //      Get the number of cpufreq steps - Each step is 200MHz
>>>         steps = get_cpufreq_steps(policy->cpuinfo.max_freq,
>>> policy->cpuinfo.min_freq);
>>> //      Malloc the required space based on number of steps
>>>         dyn_cpufreq_table = kmalloc(sizeof(struct
>>> cpufreq_frequency_table) * steps, GFP_KERNEL);
>>> //      Create entries in the table
>>>         create_freq_table(dyn_cpufreq_table, policy);
>>
>> That's okay, but i didn't get how will you get different set of
>> freqs in your table when you boot up.
>>
>>> However, when I use this code, although my debug statements suggest
>>> that the table is created successfully, sysfs shows no cpufreq under
>>> /sys/devices/system/cpu/cpu0/
>>
>> Something is wrong. And for finding out what, you need to post your code
>> here, even if it is non-mainline-able.
>>
>>
>>> Am I doing something wrong? Is GFP_KERNEL the wrong flag to use for
>>> this purpose?
>>
>> That's not the culprit for sure.
/*
 *  linux/arch/arm/mach-realview/cpufreq.c
 *
 *  Copyright (C) 2012-2013 University at Buffalo.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * CPUfreq driver for the RealView platform
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/cpufreq.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>

#include <mach/hardware.h>
#include <mach/platform.h>
#include <asm/mach-types.h>
#include <asm/hardware/realview.h>

static struct cpufreq_driver realview_driver;

#define REALVIEW_ID  	IO_ADDRESS(REALVIEW_SYS_ID)
#define REALVIEW_CPU	IO_ADDRESS(REALVIEW_SYS_OSC3)
#define REALVIEW_LOCK 	IO_ADDRESS(REALVIEW_SYS_LOCK)

static struct cpufreq_frequency_table *realview_freq_table;
/*
static struct cpufreq_frequency_table realview_freq_table[] = {
	{ 0, 100  * 1000  },
	{ 1, 200  * 1000  },
	{ 2, 400  * 1000  },
	{ 3, 800  * 1000  },
	{ 4, 1000 * 1000 },
	{ 5, 1200 * 1000 },
	{ 6, 1400 * 1000 },
	{ 7, 1800 * 1000 },
	{ 8, 2000 * 1000 },
	{ 9, CPUFREQ_TABLE_END },
};
*/

/*
 * Gets number of steps between min_freq and max_freq
 * Each step is 200MHz
 * This is used for realview_init
 */
static int get_cpufreq_steps(struct cpufreq_policy * policy) {
	int min_freq = policy->cpuinfo.min_freq;
	int max_freq = policy->cpuinfo.max_freq;
	int freq = min_freq, steps = 0;
	while(freq <= max_freq) {
		freq += 200 * 1000;
		steps++;
	}

/*
 * Explicit steps + 1 for
 * CPUFREQ_TABLE_END
 */
	steps += 1;
	printk("Number of steps :%d\n", steps);
	return steps;
}

/*
 * Creates frequency table with steps of 200MHz
 */
static void create_freq_table(struct cpufreq_policy *policy) {
	int i = 0;
	int freq = policy->cpuinfo.min_freq;
	while(freq <= policy->cpuinfo.max_freq) {
		realview_freq_table[i].index = i;
		realview_freq_table[i].frequency = freq;
		freq += 200 * 1000;
		i++;
	}
	realview_freq_table[i].index = i;
	realview_freq_table[i].frequency = CPUFREQ_TABLE_END;

	i = 0;
	while(realview_freq_table[i].frequency != CPUFREQ_TABLE_END) {
		printk("realview_freq_table[%d] =%d\n", i, realview_freq_table[i].frequency);
		i++;
	}
}

static int realview_cpufreq_verify_speed(struct cpufreq_policy *policy)
{
	if (policy->cpu != 0)
		return -EINVAL;

	return cpufreq_frequency_table_verify(policy, realview_freq_table);
}

/*
 * Validate the speed policy.
 */
static int realview_verify_policy(struct cpufreq_policy *policy)
{
	printk("RealView CPUFreq policy verification\n");
	cpufreq_verify_within_limits(policy, 
				     policy->cpuinfo.min_freq, 
				     policy->cpuinfo.max_freq);

	policy->max = (4000 * 1000) / 1000;	//Pre-defined value of 4 GHz..Let's face it..nothing's going faster than that right now

	policy->min = (100  * 1000) / 1000;	//Pre-defined value of 100MHz

	cpufreq_verify_within_limits(policy, 
				     policy->cpuinfo.min_freq, 
				     policy->cpuinfo.max_freq);

	return 0;
}


static int realview_set_target(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation)
{
	cpumask_t cpus_allowed;
	int cpu = policy->cpu;
	struct cpufreq_freqs freqs;
	u_int current_freq;
	int i;

	int ret = cpufreq_frequency_table_target(policy, realview_freq_table,
					     target_freq, relation, &i);

	if(ret != 0)
		return ret;

//	printk("realview_set_target :%u\n", realview_freq_table[i].frequency);
	/*
	 * Save this threads cpus_allowed mask.
	 */
	cpus_allowed = current->cpus_allowed;

	/*
	 * Bind to the specified CPU.  When this call returns,
	 * we should be running on the right CPU.
	 */
	set_cpus_allowed(current, cpumask_of_cpu(cpu));
	BUG_ON(cpu != smp_processor_id());

	/* get current setting */
	current_freq = __raw_readl(REALVIEW_CPU);

	freqs.old = current_freq;

	freqs.new = realview_freq_table[i].frequency;

	freqs.cpu = policy->cpu;

	if (freqs.old == freqs.new) {
		set_cpus_allowed(current, cpus_allowed);
		return 0;
	}

	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

	current_freq = __raw_readl(REALVIEW_CPU);

	__raw_writel(0xA05F, REALVIEW_LOCK);
	__raw_writel(realview_freq_table[i].frequency, REALVIEW_CPU);
	__raw_writel(0, REALVIEW_LOCK);

	/*
	 * Restore the CPUs allowed mask.
	 */
	set_cpus_allowed(current, cpus_allowed);

	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

	return 0;
}

static unsigned int realview_get(unsigned int cpu)
{
	cpumask_t cpus_allowed;
	unsigned int current_freq;


	cpus_allowed = current->cpus_allowed;

	set_cpus_allowed(current, cpumask_of_cpu(cpu));
	BUG_ON(cpu != smp_processor_id());

	/* detect memory etc. */
	current_freq = __raw_readl(REALVIEW_CPU);

	set_cpus_allowed(current, cpus_allowed);

	return current_freq;
}

static int realview_cpufreq_init(struct cpufreq_policy *policy)
{
	int steps;
	/* set default policy and cpuinfo */
	policy->cpuinfo.max_freq = realview_get(policy->cpu);	//In KHz
	policy->cpuinfo.min_freq = 200  * 1000;	//In KHz
	policy->cpuinfo.transition_latency = 100 * 1000; //In us
	policy->cur = policy->min = policy->max = realview_get(policy->cpu);

	steps = get_cpufreq_steps(policy);
	realview_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * steps, GFP_KERNEL);
	create_freq_table(policy);

	cpufreq_frequency_table_cpuinfo(policy, realview_freq_table);
	return 0;
}

static struct cpufreq_driver realview_driver = {
	.owner		= THIS_MODULE,
	.flags		= 0,
	.verify		= realview_cpufreq_verify_speed,
	.target		= realview_set_target,
	.get		= realview_get,
	.init		= realview_cpufreq_init,
	.name		= "realview",
};

static int __init realview_cpu_init(void)
{
	return cpufreq_register_driver(&realview_driver);
}

static void __exit realview_cpu_exit(void)
{
	cpufreq_unregister_driver(&realview_driver);
}

MODULE_AUTHOR ("Guru Prasad");
MODULE_DESCRIPTION ("cpufreq driver for ARM RealView CPUs");
MODULE_LICENSE ("GPL");

module_init(realview_cpu_init);
module_exit(realview_cpu_exit);

[Index of Archives]     [Linux Kernel Devel]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Forum]     [Linux SCSI]

  Powered by Linux