Hi Charles,

At first I did looked in the hrtimers API to do so, but
it seems that significant hacking would be required to provide
what is needed for vcpufreq to work; That is:

1) Steering of the timer interrupt to a given CPU
2) Making sure that the timer callback is called in an interrupt context.

I am specifically interested in getting vcpufreq to work on TI SoCs anyway,
and using dm timers was painless comparatively.

However if someone wants to take stab at implementing this in a portable
manner shouldn't be too hard, since the platform glue is nicely abstracted
with just 3 functions to implement.

Regards

-- Pantelis

On Jun 20, 2012, at 10:56 AM, Charles Garcia-Tobin wrote:

> Hi
> 
> This is exactly the sort of technique I was proposing, but I was wondering 
> whether there is a generic timer API that could be used. I was thinking 
> hrtimer but profess no knowledge here. Such an approach would make it usable 
> on all boards.
> 
> Cheers
> 
> Charles
> 
> 
>> -----Original Message-----
>> From: Amit Kucheria [mailto:amit.kuche...@linaro.org]
>> Sent: 20 June 2012 06:30
>> To: Pantelis Antoniou
>> Cc: santosh.shilim...@ti.com; linaro-dev@lists.linaro.org;
>> vbars...@dev.rtsoft.ru; Rob Lee; Charles Garcia-Tobin
>> Subject: Re: [PATCH] [CPUFREQ] VCPUfreq: Virtual CPU frequency driver.
>> 
>> Panto,
>> 
>> This looks interesting. cc'ing Rob and Charles who were interested in
>> this at Connect.
>> 
>> /Amit
>> 
>> On Thu, Jun 21, 2012 at 3:15 AM, Pantelis Antoniou
>> <pa...@antoniou-consulting.com> wrote:
>>> Many current interesting systems have no ability to simulate the upcoming
>>> bigLITTLE machines, since their cores have to be clocked at the same speeds.
>>> 
>>> Using this driver it is possible to simulate a bigLITTLE system by means
>>> of a standard (virtual) cpufreq driver.
>>> 
>>> By using a timer per core & irq affinity it is possible to do something
>>> like this:
>>> 
>>>       $ cpucycle cpu0
>>>       90403235
>>>       $ cpucycle cpu1
>>>       89810456
>>>       $ cd /sys/devices/system/cpu/cpu0/cpufreq
>>>       $ cat scaling_available_frequencies
>>>       233325 466651 699977
>>>       $ echo 466651 > scaling_setspeed
>>>       $ cpucycle cpu0
>>>       58936083
>>> 
>>> Note that the ratios are about the same so it is somewhat accurate.
>>>       4666651  /  699977 =~ 0.666
>>>       58936083 / 90403235 =~ 0.652
>>> 
>>> The available tunables available as module parameters are:
>>> 
>>> freq:
>>>       Normal maximum CPU frequency in kHz
>>>       When 0, then the platform glue layer should probe for it.
>>>       default 0
>>> 
>>> hogtime:
>>>       Amount of time in usecs that the timer interrupt handler will hog
>>>       the CPU. Note this is time spend spinning in an IRQ handler, so
>>>       it should be as low as possible. A higher value result in more
>>>       accurate simulation.
>>>       Default 100
>>> 
>>> latency:
>>>       Simulated latency in usecs of cpu freq change.
>>>       Default 500
>>> 
>>> splits:
>>>       Number of splits in the frequency value. For example when freq is
>>>       1000000 and splits is 2 then two frequency OPPs will be generated,
>>>       one in 500000 and one in 1000000.
>>> 
>>> Only one glue layer for omap2plus is provided, but it should be trivial to
>>> add more for other platforms.
>>> ---
>>> drivers/cpufreq/Kconfig         |   26 ++++
>>> drivers/cpufreq/Makefile        |    5 +
>>> drivers/cpufreq/vcpufreq-omap.c |  251
>> +++++++++++++++++++++++++++++++++++++++
>>> drivers/cpufreq/vcpufreq.c      |  216 +++++++++++++++++++++++++++++++++
>>> drivers/cpufreq/vcpufreq.h      |   25 ++++
>>> 5 files changed, 523 insertions(+), 0 deletions(-)
>>> create mode 100644 drivers/cpufreq/vcpufreq-omap.c
>>> create mode 100644 drivers/cpufreq/vcpufreq.c
>>> create mode 100644 drivers/cpufreq/vcpufreq.h
>>> 
>>> diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
>>> index e24a2a1..1fef0ad 100644
>>> --- a/drivers/cpufreq/Kconfig
>>> +++ b/drivers/cpufreq/Kconfig
>>> @@ -194,5 +194,31 @@ depends on PPC32 || PPC64
>>> source "drivers/cpufreq/Kconfig.powerpc"
>>> endmenu
>>> 
>>> +config VCPUFREQ
>>> +       bool "Virtual CPU freq driver"
>>> +       depends on CPU_FREQ
>>> +       select CPU_FREQ_TABLE
>>> +       help
>>> +         This driver implements a cycle-soaker cpufreq driver.
>>> +
>>> +         To compile this driver as a module, choose M here: the
>>> +         module will be called vcpufreq.
>>> +
>>> +         If in doubt, say N.
>>> +
>>> +if VCPUFREQ
>>> +
>>> +choice
>>> +       prompt "VCPUFREQ Platform glue Layer"
>>> +
>>> +config VCPUFREQ_OMAP2PLUS
>>> +       bool "OMAP VCPUFREQ driver"
>>> +       depends on ARCH_OMAP2PLUS
>>> +
>>> +endchoice
>>> +
>>> +endif
>>> +
>>> endif
>>> +
>>> endmenu
>>> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
>>> index 9531fc2..97d3011 100644
>>> --- a/drivers/cpufreq/Makefile
>>> +++ b/drivers/cpufreq/Makefile
>>> @@ -52,3 +52,8 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)     += omap-cpufreq.o
>>> 
>> #############################################################################
>> #####
>>> # PowerPC platform drivers
>>> obj-$(CONFIG_CPU_FREQ_MAPLE)           += maple-cpufreq.o
>>> +
>>> 
>> +#############################################################################
>> #####
>>> +# Virtual driver
>>> +obj-$(CONFIG_VCPUFREQ)                 += vcpufreq.o
>>> +obj-$(CONFIG_VCPUFREQ_OMAP2PLUS)       += vcpufreq-omap.o
>>> diff --git a/drivers/cpufreq/vcpufreq-omap.c b/drivers/cpufreq/vcpufreq-
>> omap.c
>>> new file mode 100644
>>> index 0000000..fd789c4
>>> --- /dev/null
>>> +++ b/drivers/cpufreq/vcpufreq-omap.c
>>> @@ -0,0 +1,251 @@
>>> +/*
>>> + * Copyright 2012 Pantelis Antoniou <pa...@antoniou-consulting.com>
>>> + *
>>> + * Virtual CPUFreq glue driver for OMAP2PLUS
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#define pr_fmt(fmt) "cpufreq: " fmt
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/types.h>
>>> +#include <linux/init.h>
>>> +#include <linux/cpufreq.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/err.h>
>>> +#include <linux/module.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/math64.h>
>>> +#include <linux/delay.h>
>>> +
>>> +#include <asm/smp_plat.h>
>>> +#include <asm/cpu.h>
>>> +#include <plat/cpu.h>
>>> +#include <plat/dmtimer.h>
>>> +
>>> +#include "vcpufreq.h"
>>> +
>>> +struct omap_timer_info {
>>> +       unsigned int cpu;
>>> +       struct omap_dm_timer *dm_timer;
>>> +       unsigned int irq;
>>> +       uint64_t counter;
>>> +       char irqname[16];       /* vcpufreq%d */
>>> +       unsigned int hwtimer_rate;
>>> +       unsigned int hog_delta;
>>> +};
>>> +
>>> +static DEFINE_PER_CPU(struct omap_timer_info, omap_timer);
>>> +
>>> +static irqreturn_t dm_timer_handler(int irq, void *dev_id)
>>> +{
>>> +       struct omap_timer_info *oti = dev_id;
>>> +       unsigned int status;
>>> +       unsigned int start;
>>> +
>>> +       BUG_ON(oti == NULL);
>>> +       BUG_ON(oti->dm_timer == NULL);
>>> +
>>> +       status = omap_dm_timer_read_status(oti->dm_timer);
>>> +       if (status & OMAP_TIMER_INT_OVERFLOW) {
>>> +               omap_dm_timer_write_status(oti->dm_timer,
>>> +                               OMAP_TIMER_INT_OVERFLOW);
>>> +               omap_dm_timer_read_status(oti->dm_timer);
>>> +               oti->counter++;
>>> +
>>> +               /*
>>> +                * udelay is really crap for this; no accuracy whatsoever
>>> +                * so use the nice hardware counter and be happy
>>> +                */
>>> +               start = omap_dm_timer_read_counter(oti->dm_timer);
>>> +               while ((omap_dm_timer_read_counter(oti->dm_timer) - start)
>>> +                               < oti->hog_delta)
>>> +                       ;       /* do nothing */
>>> +
>>> +               return IRQ_HANDLED;
>>> +       }
>>> +
>>> +       return IRQ_NONE;
>>> +}
>>> +
>>> +int vcpufreq_glue_set_freq(unsigned int cpu, unsigned int new_freq,
>>> +               unsigned int old_freq)
>>> +{
>>> +       struct omap_timer_info __percpu *oti = &per_cpu(omap_timer, cpu);
>>> +       int ret = 0;
>>> +       uint32_t rate;
>>> +       unsigned int hog_timer_rate;
>>> +       unsigned int freq = vcpufreq_get_maxspeed();
>>> +       unsigned int hogtime = vcpufreq_get_hogtime();
>>> +
>>> +       /* should never happen; checked before */
>>> +       BUG_ON(new_freq == old_freq);
>>> +
>>> +       /* max freq; stop the timer */
>>> +       if (new_freq == freq) {
>>> +               pr_debug("#%d: shut down timer\n", cpu);
>>> +               /* no error */
>>> +               ret = 0;
>>> +               goto omap_stop_timer;
>>> +
>>> +       }
>>> +
>>> +       /* timer was stopped, we should start it */
>>> +       if (old_freq == freq) {
>>> +
>>> +               oti->cpu = cpu;
>>> +
>>> +               /* get any omap timer */
>>> +               oti->dm_timer = omap_dm_timer_request();
>>> +               if (oti->dm_timer == NULL) {
>>> +                       pr_err("#%d: No available omap timers\n", cpu);
>>> +                       ret = -ENODEV;
>>> +                       goto omap_stop_timer;
>>> +               }
>>> +
>>> +               pr_debug("#%d: got omap timer with id %d\n", cpu, oti-
>>> dm_timer->id);
>>> +
>>> +               /* source it from SYS_CLK */
>>> +               ret = omap_dm_timer_set_source(oti->dm_timer,
>> OMAP_TIMER_SRC_SYS_CLK);
>>> +               if (ret != 0) {
>>> +                       pr_err("#%d: omap_dm_timer_set_source() failed\n",
>> cpu);
>>> +                       goto omap_stop_timer;
>>> +               }
>>> +
>>> +               /* set the prescaler to 0 (need a fast timer) */
>>> +               ret = omap_dm_timer_set_prescaler(oti->dm_timer, 0);
>>> +               if (ret != 0) {
>>> +                       pr_err("#%d: omap_dm_timer_set_prescaler()
>> failed\n", cpu);
>>> +                       goto omap_stop_timer;
>>> +               }
>>> +
>>> +               /* get the irq */
>>> +               ret = omap_dm_timer_get_irq(oti->dm_timer);
>>> +               if (ret < 0) {
>>> +                       pr_err("#%d: omap_dm_timer_get_irq() failed\n",
>> cpu);
>>> +                       goto omap_stop_timer;
>>> +               }
>>> +               oti->irq = ret;
>>> +
>>> +               snprintf(oti->irqname, sizeof(oti->irqname), "vcpufreq%u",
>> cpu);
>>> +               ret = request_irq(oti->irq, dm_timer_handler,
>>> +                               IRQF_DISABLED | IRQF_TIMER, oti->irqname,
>> oti);
>>> +               if (ret < 0) {
>>> +                       pr_err("#%d: failed to request percpu irq %d
>> (%d)\n", cpu,
>>> +                                       oti->irq, ret);
>>> +                       goto omap_stop_timer;
>>> +               }
>>> +       } else
>>> +               omap_dm_timer_stop(oti->dm_timer);
>>> +
>>> +       /* common in either case */
>>> +       oti->hwtimer_rate = clk_get_rate(omap_dm_timer_get_fclk(oti-
>>> dm_timer));
>>> +       if (oti->hwtimer_rate == 0) {
>>> +               pr_err("#%d: illegal timer fclk rate\n", cpu);
>>> +               goto omap_stop_timer;
>>> +       }
>>> +       pr_debug("#%d: hwtimer_rate=%u/sec (period %uns)", cpu,
>>> +                       oti->hwtimer_rate, 1000000000 / oti->hwtimer_rate);
>>> +
>>> +       oti->hog_delta = div_u64((u64)oti->hwtimer_rate * (u64)hogtime,
>> 1000000);
>>> +       pr_debug("#%d: hog_delta = %u\n", cpu, oti->hog_delta);
>>> +
>>> +       /* rate of hog timer */
>>> +       hog_timer_rate = div_u64((u64)(freq - new_freq) * 1000000, freq *
>> hogtime);
>>> +       pr_debug("#%d: hog timer rate = %u\n", cpu, hog_timer_rate);
>>> +
>>> +       rate = (oti->hwtimer_rate + (hog_timer_rate / 2)) / hog_timer_rate;
>>> +       pr_debug("#%d: hw timer rate = %u\n", cpu, rate);
>>> +
>>> +       omap_dm_timer_set_load(oti->dm_timer, 1, 0xFFFFFFFF - rate);
>>> +
>>> +       /* first start */
>>> +       if (old_freq == freq) {
>>> +               /* enable the interrupt on overflow */
>>> +               omap_dm_timer_set_int_enable(oti->dm_timer,
>>> +                               OMAP_TIMER_INT_OVERFLOW);
>>> +               /* route the interrupt to a given cpu */
>>> +               irq_set_affinity(oti->irq, cpumask_of(cpu));
>>> +       }
>>> +
>>> +       omap_dm_timer_start(oti->dm_timer);
>>> +
>>> +       vcpufreq_set_speed(cpu, new_freq);
>>> +       return 0;
>>> +
>>> +omap_stop_timer:
>>> +       /* clear everything */
>>> +       if (oti->dm_timer) {
>>> +
>>> +               omap_dm_timer_stop(oti->dm_timer);
>>> +               if (oti->irq != (unsigned int)-1) {
>>> +                       free_irq(oti->irq, oti);
>>> +                       oti->irq = -1;
>>> +               }
>>> +               omap_dm_timer_free(oti->dm_timer);
>>> +
>>> +               /* clean up */
>>> +               memset(oti, 0, sizeof(*oti));
>>> +               oti->irq = (unsigned int)-1;
>>> +       }
>>> +
>>> +       /* always return to max speed here */
>>> +       vcpufreq_set_speed(cpu, freq);
>>> +       return ret;
>>> +}
>>> +
>>> +int vcpufreq_glue_init(struct cpufreq_policy *policy, int *freq)
>>> +{
>>> +       struct omap_timer_info __percpu *oti;
>>> +       int ret = 0;
>>> +       struct clk *mpu_clk;
>>> +       const char *mpu_clk_name = NULL;
>>> +
>>> +       BUG_ON(freq == NULL);
>>> +
>>> +       /* if no freq was provided, probe */
>>> +       if (*freq == 0) {
>>> +               if (cpu_is_omap24xx())
>>> +                       mpu_clk_name = "virt_prcm_set";
>>> +               else if (cpu_is_omap34xx())
>>> +                       mpu_clk_name = "dpll1_ck";
>>> +               else if (cpu_is_omap44xx())
>>> +                       mpu_clk_name = "dpll_mpu_ck";
>>> +
>>> +               if (mpu_clk_name == NULL) {
>>> +                       pr_err("%s: Unknown mpu_clk_name (unsupported)\n",
>>> +                                       __func__);
>>> +                       ret = -EINVAL;
>>> +                       goto error_out;
>>> +               }
>>> +               mpu_clk = clk_get(NULL, mpu_clk_name);
>>> +               if (IS_ERR(mpu_clk)) {
>>> +                       ret = PTR_ERR(mpu_clk);
>>> +                       pr_err("%s: clk_get for '%s' failed\n", __func__,
>>> +                                       mpu_clk_name);
>>> +                       goto error_out;
>>> +               }
>>> +               /* update freq */
>>> +               *freq = clk_get_rate(mpu_clk) / 1000;
>>> +       }
>>> +
>>> +       /* initialize per cpu structure */
>>> +       oti = &per_cpu(omap_timer, policy->cpu);
>>> +       memset(oti, 0, sizeof(*oti));
>>> +       oti->irq = (unsigned int)-1;
>>> +
>>> +       ret = 0;
>>> +
>>> +error_out:
>>> +       return ret;
>>> +}
>>> +
>>> +int vcpufreq_glue_exit(struct cpufreq_policy *policy)
>>> +{
>>> +       return 0;
>>> +}
>>> diff --git a/drivers/cpufreq/vcpufreq.c b/drivers/cpufreq/vcpufreq.c
>>> new file mode 100644
>>> index 0000000..b5ded3f
>>> --- /dev/null
>>> +++ b/drivers/cpufreq/vcpufreq.c
>>> @@ -0,0 +1,216 @@
>>> +/*
>>> + * Copyright 2012 Pantelis Antoniou <pa...@antoniou-consulting.com>
>>> + *
>>> + * Virtual CPUFreq driver; allows usage of normal SMP systems for
>>> + * asymmetric processing evaluation.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#define pr_fmt(fmt) "cpufreq: " fmt
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/types.h>
>>> +#include <linux/init.h>
>>> +#include <linux/cpufreq.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/err.h>
>>> +#include <linux/module.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/math64.h>
>>> +#include <linux/delay.h>
>>> +
>>> +#include "vcpufreq.h"
>>> +
>>> +static struct cpufreq_frequency_table *vfreq_table = NULL;
>>> +
>>> +static unsigned int latency = 500;
>>> +static unsigned int splits = 3;
>>> +static unsigned int freq = 0;  /* default 1GHz */
>>> +static unsigned int hogtime = 100;
>>> +
>>> +static DEFINE_PER_CPU(unsigned int, curfreq);
>>> +
>>> +static int vcpufreq_verify_speed(struct cpufreq_policy *policy)
>>> +{
>>> +       BUG_ON(vfreq_table == NULL);
>>> +       return cpufreq_frequency_table_verify(policy, vfreq_table);
>>> +}
>>> +
>>> +unsigned int vcpufreq_get_speed(unsigned int cpu)
>>> +{
>>> +       return per_cpu(curfreq, cpu);
>>> +}
>>> +
>>> +void vcpufreq_set_speed(unsigned int cpu, unsigned int new_freq)
>>> +{
>>> +       per_cpu(curfreq, cpu) = new_freq;
>>> +}
>>> +
>>> +unsigned int vcpufreq_get_maxspeed(void)
>>> +{
>>> +       return freq;
>>> +}
>>> +
>>> +unsigned int vcpufreq_get_hogtime(void)
>>> +{
>>> +       return hogtime;
>>> +}
>>> +
>>> +static int vcpufreq_set_target(struct cpufreq_policy *policy,
>>> +                                     unsigned int target_freq,
>>> +                                     unsigned int relation)
>>> +{
>>> +       int ret;
>>> +       unsigned int i;
>>> +       struct cpufreq_freqs freqs;
>>> +
>>> +       BUG_ON(vfreq_table == NULL);
>>> +
>>> +       ret = cpufreq_frequency_table_target(policy, vfreq_table,
>>> +                                            target_freq, relation, &i);
>>> +       if (ret != 0)
>>> +               return ret;
>>> +
>>> +       memset(&freqs, 0, sizeof(freqs));
>>> +       freqs.cpu = policy->cpu;
>>> +       freqs.old = vcpufreq_get_speed(policy->cpu);
>>> +       freqs.new = vfreq_table[i].frequency;
>>> +
>>> +       if (freqs.old == freqs.new && policy->cur == freqs.new)
>>> +               return 0;
>>> +
>>> +       /* the CPUs are free-clocked */
>>> +       freqs.cpu = policy->cpu;
>>> +       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
>>> +
>>> +       pr_debug("Transition %d-%dkHz\n", freqs.old, freqs.new);
>>> +
>>> +       /* nothing */
>>> +       if (freqs.new == freqs.old) {
>>> +               pr_err("#%d: same freq %u\n", policy->cpu, freqs.new);
>>> +               ret = -EAGAIN;
>>> +               goto error_out;
>>> +       }
>>> +
>>> +       ret = vcpufreq_glue_set_freq(policy->cpu, freqs.new, freqs.old);
>>> +
>>> +error_out:
>>> +       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static int __cpuinit vcpufreq_driver_init(struct cpufreq_policy *policy)
>>> +{
>>> +       int ret;
>>> +       unsigned int i;
>>> +
>>> +       ret = vcpufreq_glue_init(policy, &freq);
>>> +       if (ret != 0) {
>>> +               pr_err("%s: vcpufreq_glue_init() failed\n", __func__);
>>> +               goto error_out;
>>> +       }
>>> +
>>> +       if (splits < 1) {
>>> +               pr_err("%s: Illegal splits value (%u)\n", __func__, splits);
>>> +               ret = -EINVAL;
>>> +               goto error_out;
>>> +       }
>>> +
>>> +       vfreq_table = kmalloc(sizeof(*vfreq_table) * (splits + 1),
>> GFP_KERNEL);
>>> +       if (vfreq_table == NULL) {
>>> +               pr_err("Failed to allocate frequency table: %d\n",
>>> +                      ret);
>>> +               ret = -ENOMEM;
>>> +               goto error_out;
>>> +       }
>>> +
>>> +       /* 0 .. splits-1 */
>>> +       for (i = 0; i < splits; i++) {
>>> +               vfreq_table[i].index = i;
>>> +               vfreq_table[i].frequency = (freq * (i + 1)) / splits;
>>> +       }
>>> +       /* splits-1 */
>>> +       vfreq_table[i].index = i;
>>> +       vfreq_table[i].frequency = freq;
>>> +
>>> +       /* ends */
>>> +       vfreq_table[i].index = i;
>>> +       vfreq_table[i].frequency = CPUFREQ_TABLE_END;
>>> +
>>> +       ret = cpufreq_frequency_table_cpuinfo(policy, vfreq_table);
>>> +       if (ret != 0) {
>>> +               pr_err("Failed to configure frequency table: %d\n",
>>> +                      ret);
>>> +               goto error_out;
>>> +       }
>>> +
>>> +       cpufreq_frequency_table_get_attr(vfreq_table, policy->cpu);
>>> +
>>> +       policy->min = policy->cpuinfo.min_freq;
>>> +       policy->max = policy->cpuinfo.max_freq;
>>> +
>>> +       /* always start at the max */
>>> +       per_cpu(curfreq, policy->cpu) = freq;
>>> +
>>> +       policy->cur = per_cpu(curfreq, policy->cpu);
>>> +       policy->cpuinfo.transition_latency = latency;
>>> +
>>> +       pr_info("#%d: Virtual CPU frequency driver initialized\n", policy-
>>> cpu);
>>> +
>>> +       return 0;
>>> +
>>> +error_out:
>>> +       kfree(vfreq_table);
>>> +       vfreq_table = NULL;
>>> +       return ret;
>>> +}
>>> +
>>> +static int __cpuexit vcpufreq_driver_exit(struct cpufreq_policy *policy)
>>> +{
>>> +       kfree(vfreq_table);
>>> +       vfreq_table = NULL;
>>> +       vcpufreq_glue_exit(policy);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static struct freq_attr *vcpufreq_attr[] = {
>>> +       &cpufreq_freq_attr_scaling_available_freqs,
>>> +       NULL,
>>> +};
>>> +
>>> +static struct cpufreq_driver vcpufreq_driver = {
>>> +       .owner          = THIS_MODULE,
>>> +       .flags          = CPUFREQ_CONST_LOOPS,
>>> +       .verify         = vcpufreq_verify_speed,
>>> +       .target         = vcpufreq_set_target,
>>> +       .get            = vcpufreq_get_speed,
>>> +       .init           = vcpufreq_driver_init,
>>> +       .exit           = vcpufreq_driver_exit,
>>> +       .name           = "vcpufreq",
>>> +       .attr           = vcpufreq_attr,
>>> +};
>>> +
>>> +static int __init vcpufreq_init(void)
>>> +{
>>> +       return cpufreq_register_driver(&vcpufreq_driver);
>>> +}
>>> +module_init(vcpufreq_init);
>>> +
>>> +module_param(latency, uint, 0644);
>>> +MODULE_PARM_DESC(latency, "Transition latency in usecs (default 500)");
>>> +
>>> +module_param(splits, uint, 0644);
>>> +MODULE_PARM_DESC(splits, "Number of frequency splits (default 2)");
>>> +
>>> +module_param(freq, uint, 0644);
>>> +MODULE_PARM_DESC(freq, "Maximum frequency in kHz (0 means platform
>> detect)");
>>> +
>>> +module_param(hogtime, uint, 0644);
>>> +MODULE_PARM_DESC(hogtime, "Time spend hogging the CPU in the IRQ handle in
>> usec (default 10)");
>>> diff --git a/drivers/cpufreq/vcpufreq.h b/drivers/cpufreq/vcpufreq.h
>>> new file mode 100644
>>> index 0000000..6135b23
>>> --- /dev/null
>>> +++ b/drivers/cpufreq/vcpufreq.h
>>> @@ -0,0 +1,25 @@
>>> +#ifndef __VCPUFREQ_H
>>> +#define __VCPUFREQ_H
>>> +
>>> +/*
>>> + * Copyright 2012 Pantelis Antoniou <pa...@antoniou-consulting.com>
>>> + *
>>> + * Virtual CPUFreq driver header.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +/* provided by the glue layer */
>>> +int vcpufreq_glue_set_freq(unsigned int cpu, unsigned int new_freq,
>>> +               unsigned int old_freq);
>>> +int vcpufreq_glue_init(struct cpufreq_policy *policy, int *freq);
>>> +int vcpufreq_glue_exit(struct cpufreq_policy *policy);
>>> +
>>> +/* provided by the core */
>>> +unsigned int vcpufreq_get_maxspeed(void);
>>> +unsigned int vcpufreq_get_hogtime(void);
>>> +void vcpufreq_set_speed(unsigned int cpu, unsigned int new_freq);
>>> +
>>> +#endif
>>> --
>>> 1.7.1
>>> 
>>> 
>>> _______________________________________________
>>> linaro-dev mailing list
>>> linaro-dev@lists.linaro.org
>>> http://lists.linaro.org/mailman/listinfo/linaro-dev
> 
> 
> -- IMPORTANT NOTICE: The contents of this email and any attachments are 
> confidential and may also be privileged. If you are not the intended 
> recipient, please notify the sender immediately and do not disclose the 
> contents to any other person, use it for any purpose, or store or copy the 
> information in any medium.  Thank you.
> 


_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to