diff mbox

[v4,09/10] cpufreq: add generic CPUFreq driver for DaVinci

Message ID 1251713887-2824-9-git-send-email-nsekhar@ti.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Sekhar Nori Aug. 31, 2009, 10:18 a.m. UTC
Adds a basic CPUFreq driver for DaVinci devices registering with the
kernel CPUFreq infrastructure.

Signed-off-by: Sekhar Nori <nsekhar@ti.com>
---
 arch/arm/Kconfig                            |    2 +-
 arch/arm/mach-davinci/Makefile              |    3 +
 arch/arm/mach-davinci/cpufreq.c             |  183 +++++++++++++++++++++++++++
 arch/arm/mach-davinci/include/mach/common.h |    3 +
 4 files changed, 190 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-davinci/cpufreq.c

Comments

Kevin Hilman Sept. 14, 2009, 9:28 p.m. UTC | #1
Sekhar Nori <nsekhar@ti.com> writes:

> Adds a basic CPUFreq driver for DaVinci devices registering with the
> kernel CPUFreq infrastructure.
>
> Signed-off-by: Sekhar Nori <nsekhar@ti.com>

This looks mostly ok, but some comments below...

Also, when you re-spin, go ahead and fold 
[PATCH 1/4] davinci: cpufreq: use cpufreq_frequency_table_target() to search freq table
into this series of cpufreq core support.

> ---
>  arch/arm/Kconfig                            |    2 +-
>  arch/arm/mach-davinci/Makefile              |    3 +
>  arch/arm/mach-davinci/cpufreq.c             |  183 +++++++++++++++++++++++++++
>  arch/arm/mach-davinci/include/mach/common.h |    3 +
>  4 files changed, 190 insertions(+), 1 deletions(-)
>  create mode 100644 arch/arm/mach-davinci/cpufreq.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index aef63c8..38482a6 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1241,7 +1241,7 @@ endmenu
>  
>  menu "CPU Power Management"
>  
> -if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX)
> +if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX || ARCH_DAVINCI_DA8XX)
>  
>  source "drivers/cpufreq/Kconfig"
>  
> diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
> index 2e11e84..be629c5 100644
> --- a/arch/arm/mach-davinci/Makefile
> +++ b/arch/arm/mach-davinci/Makefile
> @@ -29,3 +29,6 @@ obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM)	+= board-dm646x-evm.o
>  obj-$(CONFIG_MACH_DAVINCI_DM365_EVM)	+= board-dm365-evm.o
>  obj-$(CONFIG_MACH_DAVINCI_DA830_EVM)	+= board-da830-evm.o
>  obj-$(CONFIG_MACH_DAVINCI_DA850_EVM)	+= board-da850-evm.o
> +
> +# Power Management
> +obj-$(CONFIG_CPU_FREQ)			+= cpufreq.o
> diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c
> new file mode 100644
> index 0000000..65393b9
> --- /dev/null
> +++ b/arch/arm/mach-davinci/cpufreq.c
> @@ -0,0 +1,183 @@
> +/*
> + * CPU frequency scaling for DaVinci
> + *
> + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
> + *
> + *  Copyright (C) 2005 Nokia Corporation
> + *  Written by Tony Lindgren <tony@atomide.com>
> + *
> + *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
> + *
> + * Copyright (C) 2007-2008 Texas Instruments, Inc.
> + * Updated to support OMAP3
> + * Rajendra Nayak <rnayak@ti.com>
> + *
> + * 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.
> + */
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/cpufreq.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +
> +#include <mach/hardware.h>
> +#include <asm/system.h>
> +#include <mach/clock.h>
> +#include <mach/common.h>
> +
> +#include "clock.h"
> +
> +static struct cpufreq_frequency_table *freq_table;
> +static struct clk *armclk;
> +
> +static int davinci_verify_speed(struct cpufreq_policy *policy)
> +{
> +	if (freq_table)
> +		return cpufreq_frequency_table_verify(policy, freq_table);
> +
> +	if (policy->cpu)
> +		return -EINVAL;
> +
> +	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
> +				     policy->cpuinfo.max_freq);
> +
> +	policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000;
> +	policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000;
> +	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
> +				     policy->cpuinfo.max_freq);
> +	return 0;
> +}
> +
> +static unsigned int davinci_getspeed(unsigned int cpu)
> +{
> +	unsigned long rate;
> +
> +	if (cpu)
> +		return 0;
> +
> +	rate = clk_get_rate(armclk) / 1000;
> +
> +	return rate;
> +}
> +
> +static int davinci_target(struct cpufreq_policy *policy,
> +		       unsigned int target_freq,
> +		       unsigned int relation)
> +{
> +	struct cpufreq_freqs freqs;
> +	int ret = 0;
> +
> +	/*
> +	 * Ensure desired rate is within allowed range.  Some govenors
> +	 * (ondemand) will just pass target_freq=0 to get the minimum.
> +	 */
> +	if (target_freq < policy->cpuinfo.min_freq)
> +		target_freq = policy->cpuinfo.min_freq;
> +	if (target_freq > policy->cpuinfo.max_freq)
> +		target_freq = policy->cpuinfo.max_freq;
> +
> +	freqs.old = davinci_getspeed(0);
> +	freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000;
> +	freqs.cpu = 0;
> +
> +	if (freqs.old == freqs.new)
> +		return ret;
> +	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
> +#ifdef CONFIG_CPU_FREQ_DEBUG
> +	printk(KERN_DEBUG "cpufreq-davinci: transition: %u --> %u\n",
> +	       freqs.old, freqs.new);
> +#endif
> +	ret = clk_set_rate(armclk, freqs.new * 1000);
> +	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
> +
> +	return ret;
> +}
> +
> +static int __init davinci_cpu_init(struct cpufreq_policy *policy)
> +{
> +	int result = 0;
> +
> +	armclk = clk_get(NULL, "arm");
> +	if (IS_ERR(armclk)) {
> +		printk(KERN_ERR "cpufreq-davinci: Unable to get ARM clock\n");
> +		return PTR_ERR(armclk);
> +	}
> +
> +	if (policy->cpu != 0) {
> +		clk_put(armclk);
> +		return -EINVAL;
> +	}
> +
> +	policy->cur = policy->min = policy->max = davinci_getspeed(0);
> +
> +	davinci_soc_info.init_cpufreq_table(&freq_table);

Should probably check for non-NULL pointer here as most platforms this
will be NULL, and folks will probably try enabling CPUfreq on platforms
that don't support it.

However, I'm not crazy about adding this level of function call
abstraction to SoC info.  I want to keep that small and simple and
focused on SoC specifics, and not an API.

Kevin

> +	if (freq_table) {
> +		result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
> +		if (!result)
> +			cpufreq_frequency_table_get_attr(freq_table,
> +							policy->cpu);
> +	} else {
> +		policy->cpuinfo.min_freq = policy->min;
> +		policy->cpuinfo.max_freq = policy->max;
> +	}
> +
> +	clk_set_rate(armclk, policy->cpuinfo.max_freq * 1000);
> +
> +	policy->min = policy->cpuinfo.min_freq;
> +	policy->max = policy->cpuinfo.max_freq;
> +	policy->cur = davinci_getspeed(0);
> +
> +	/*
> +	 * Time measurement across the target() function yields
> +	 * ~500 us time taken with no drivers on notification list.
> +	 * Setting the latency to 700 us to accomodate addition of
> +	 * drivers to pre/post change notification list.
> +	 */
> +	policy->cpuinfo.transition_latency = 700 * 1000;
> +	return 0;
> +}
> +
> +static int davinci_cpu_exit(struct cpufreq_policy *policy)
> +{
> +	clk_put(armclk);
> +	return 0;
> +}
> +
> +static struct freq_attr *davinci_cpufreq_attr[] = {
> +	&cpufreq_freq_attr_scaling_available_freqs,
> +	NULL,
> +};
> +
> +static struct cpufreq_driver davinci_driver = {
> +	.flags		= CPUFREQ_STICKY,
> +	.verify		= davinci_verify_speed,
> +	.target		= davinci_target,
> +	.get		= davinci_getspeed,
> +	.init		= davinci_cpu_init,
> +	.exit		= davinci_cpu_exit,
> +	.name		= "davinci",
> +	.attr		= davinci_cpufreq_attr,
> +};
> +
> +static int __init davinci_cpufreq_init(void)
> +{
> +	return cpufreq_register_driver(&davinci_driver);
> +}
> +
> +late_initcall(davinci_cpufreq_init);
> +
> +/*
> + * if ever we want to remove this, upon cleanup call:
> + *
> + * cpufreq_unregister_driver()
> + * cpufreq_frequency_table_put_attr()
> + */
> +
> diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h
> index 1fd3917..1487a57 100644
> --- a/arch/arm/mach-davinci/include/mach/common.h
> +++ b/arch/arm/mach-davinci/include/mach/common.h
> @@ -12,6 +12,8 @@
>  #ifndef __ARCH_ARM_MACH_DAVINCI_COMMON_H
>  #define __ARCH_ARM_MACH_DAVINCI_COMMON_H
>  
> +#include <linux/cpufreq.h>
> +
>  struct sys_timer;
>  
>  extern struct sys_timer davinci_timer;
> @@ -68,6 +70,7 @@ struct davinci_soc_info {
>  	struct emac_platform_data	*emac_pdata;
>  	dma_addr_t			sram_dma;
>  	unsigned			sram_len;
> +	void (*init_cpufreq_table) (struct cpufreq_frequency_table **);
>  };
>  
>  extern struct davinci_soc_info davinci_soc_info;
> -- 
> 1.6.2.4
>
> _______________________________________________
> Davinci-linux-open-source mailing list
> Davinci-linux-open-source@linux.davincidsp.com
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
Sekhar Nori Sept. 15, 2009, 9:51 a.m. UTC | #2
On Tue, Sep 15, 2009 at 02:58:28, Kevin Hilman wrote:
> Sekhar Nori <nsekhar@ti.com> writes:
>
> > Adds a basic CPUFreq driver for DaVinci devices registering with the
> > kernel CPUFreq infrastructure.
> >
> > Signed-off-by: Sekhar Nori <nsekhar@ti.com>

[...]

> > diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c
> > new file mode 100644
> > index 0000000..65393b9
> > --- /dev/null
> > +++ b/arch/arm/mach-davinci/cpufreq.c

[...]

> > +static int __init davinci_cpu_init(struct cpufreq_policy *policy)
> > +{
> > +   int result = 0;
> > +
> > +   armclk = clk_get(NULL, "arm");
> > +   if (IS_ERR(armclk)) {
> > +           printk(KERN_ERR "cpufreq-davinci: Unable to get ARM clock\n");
> > +           return PTR_ERR(armclk);
> > +   }
> > +
> > +   if (policy->cpu != 0) {
> > +           clk_put(armclk);
> > +           return -EINVAL;
> > +   }
> > +
> > +   policy->cur = policy->min = policy->max = davinci_getspeed(0);
> > +
> > +   davinci_soc_info.init_cpufreq_table(&freq_table);
>
> Should probably check for non-NULL pointer here as most platforms this
> will be NULL, and folks will probably try enabling CPUfreq on platforms
> that don't support it.
>
> However, I'm not crazy about adding this level of function call
> abstraction to SoC info.  I want to keep that small and simple and
> focused on SoC specifics, and not an API.

Okay. Will take care of this when I resend.

Thanks,
Sekhar
diff mbox

Patch

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index aef63c8..38482a6 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1241,7 +1241,7 @@  endmenu
 
 menu "CPU Power Management"
 
-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX)
+if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX || ARCH_DAVINCI_DA8XX)
 
 source "drivers/cpufreq/Kconfig"
 
diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
index 2e11e84..be629c5 100644
--- a/arch/arm/mach-davinci/Makefile
+++ b/arch/arm/mach-davinci/Makefile
@@ -29,3 +29,6 @@  obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM)	+= board-dm646x-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DM365_EVM)	+= board-dm365-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DA830_EVM)	+= board-da830-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DA850_EVM)	+= board-da850-evm.o
+
+# Power Management
+obj-$(CONFIG_CPU_FREQ)			+= cpufreq.o
diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c
new file mode 100644
index 0000000..65393b9
--- /dev/null
+++ b/arch/arm/mach-davinci/cpufreq.c
@@ -0,0 +1,183 @@ 
+/*
+ * CPU frequency scaling for DaVinci
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ *  Written by Tony Lindgren <tony@atomide.com>
+ *
+ *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Updated to support OMAP3
+ * Rajendra Nayak <rnayak@ti.com>
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include <mach/common.h>
+
+#include "clock.h"
+
+static struct cpufreq_frequency_table *freq_table;
+static struct clk *armclk;
+
+static int davinci_verify_speed(struct cpufreq_policy *policy)
+{
+	if (freq_table)
+		return cpufreq_frequency_table_verify(policy, freq_table);
+
+	if (policy->cpu)
+		return -EINVAL;
+
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+				     policy->cpuinfo.max_freq);
+
+	policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000;
+	policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000;
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+				     policy->cpuinfo.max_freq);
+	return 0;
+}
+
+static unsigned int davinci_getspeed(unsigned int cpu)
+{
+	unsigned long rate;
+
+	if (cpu)
+		return 0;
+
+	rate = clk_get_rate(armclk) / 1000;
+
+	return rate;
+}
+
+static int davinci_target(struct cpufreq_policy *policy,
+		       unsigned int target_freq,
+		       unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	int ret = 0;
+
+	/*
+	 * Ensure desired rate is within allowed range.  Some govenors
+	 * (ondemand) will just pass target_freq=0 to get the minimum.
+	 */
+	if (target_freq < policy->cpuinfo.min_freq)
+		target_freq = policy->cpuinfo.min_freq;
+	if (target_freq > policy->cpuinfo.max_freq)
+		target_freq = policy->cpuinfo.max_freq;
+
+	freqs.old = davinci_getspeed(0);
+	freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000;
+	freqs.cpu = 0;
+
+	if (freqs.old == freqs.new)
+		return ret;
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+#ifdef CONFIG_CPU_FREQ_DEBUG
+	printk(KERN_DEBUG "cpufreq-davinci: transition: %u --> %u\n",
+	       freqs.old, freqs.new);
+#endif
+	ret = clk_set_rate(armclk, freqs.new * 1000);
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return ret;
+}
+
+static int __init davinci_cpu_init(struct cpufreq_policy *policy)
+{
+	int result = 0;
+
+	armclk = clk_get(NULL, "arm");
+	if (IS_ERR(armclk)) {
+		printk(KERN_ERR "cpufreq-davinci: Unable to get ARM clock\n");
+		return PTR_ERR(armclk);
+	}
+
+	if (policy->cpu != 0) {
+		clk_put(armclk);
+		return -EINVAL;
+	}
+
+	policy->cur = policy->min = policy->max = davinci_getspeed(0);
+
+	davinci_soc_info.init_cpufreq_table(&freq_table);
+	if (freq_table) {
+		result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+		if (!result)
+			cpufreq_frequency_table_get_attr(freq_table,
+							policy->cpu);
+	} else {
+		policy->cpuinfo.min_freq = policy->min;
+		policy->cpuinfo.max_freq = policy->max;
+	}
+
+	clk_set_rate(armclk, policy->cpuinfo.max_freq * 1000);
+
+	policy->min = policy->cpuinfo.min_freq;
+	policy->max = policy->cpuinfo.max_freq;
+	policy->cur = davinci_getspeed(0);
+
+	/*
+	 * Time measurement across the target() function yields
+	 * ~500 us time taken with no drivers on notification list.
+	 * Setting the latency to 700 us to accomodate addition of
+	 * drivers to pre/post change notification list.
+	 */
+	policy->cpuinfo.transition_latency = 700 * 1000;
+	return 0;
+}
+
+static int davinci_cpu_exit(struct cpufreq_policy *policy)
+{
+	clk_put(armclk);
+	return 0;
+}
+
+static struct freq_attr *davinci_cpufreq_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+static struct cpufreq_driver davinci_driver = {
+	.flags		= CPUFREQ_STICKY,
+	.verify		= davinci_verify_speed,
+	.target		= davinci_target,
+	.get		= davinci_getspeed,
+	.init		= davinci_cpu_init,
+	.exit		= davinci_cpu_exit,
+	.name		= "davinci",
+	.attr		= davinci_cpufreq_attr,
+};
+
+static int __init davinci_cpufreq_init(void)
+{
+	return cpufreq_register_driver(&davinci_driver);
+}
+
+late_initcall(davinci_cpufreq_init);
+
+/*
+ * if ever we want to remove this, upon cleanup call:
+ *
+ * cpufreq_unregister_driver()
+ * cpufreq_frequency_table_put_attr()
+ */
+
diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h
index 1fd3917..1487a57 100644
--- a/arch/arm/mach-davinci/include/mach/common.h
+++ b/arch/arm/mach-davinci/include/mach/common.h
@@ -12,6 +12,8 @@ 
 #ifndef __ARCH_ARM_MACH_DAVINCI_COMMON_H
 #define __ARCH_ARM_MACH_DAVINCI_COMMON_H
 
+#include <linux/cpufreq.h>
+
 struct sys_timer;
 
 extern struct sys_timer davinci_timer;
@@ -68,6 +70,7 @@  struct davinci_soc_info {
 	struct emac_platform_data	*emac_pdata;
 	dma_addr_t			sram_dma;
 	unsigned			sram_len;
+	void (*init_cpufreq_table) (struct cpufreq_frequency_table **);
 };
 
 extern struct davinci_soc_info davinci_soc_info;