linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [2.6.22] pasemi: cpufreq driver
@ 2007-04-25 20:46 Olof Johansson
  2007-04-25 23:47 ` Arnd Bergmann
  2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
  0 siblings, 2 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-25 20:46 UTC (permalink / raw)
  To: paulus; +Cc: linuxppc-dev, egor, cpufreq

Cpufreq driver for PA Semi PWRficient processors.

Signed-off-by: Egor Martovetsky <egor@pasemi.com>
Signed-off-by: Olof Johansson <olof@lixom.net>


Index: powerpc/arch/powerpc/platforms/pasemi/Makefile
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/Makefile
+++ powerpc/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,3 @@
 obj-y	+= setup.o pci.o time.o idle.o powersave.o iommu.o gpio_mdio.o
 
+obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o
Index: powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
===================================================================
--- /dev/null
+++ powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Author: Egor Martovetsky <egor@pasemi.com>
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/timer.h>
+
+#include <asm/hw_irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#define SDCASR_REG		0x0100
+#define SDCASR_REG_STRIDE	0x1000
+#define SDCPWR_CFGA0_REG	0x0100
+#define SDCPWR_PWST0_REG	0x0000
+#define SDCPWR_GIZTIME_REG	0x0440
+
+/* SDCPWR_GIZTIME_REG fields */
+#define SDCPWR_GIZTIME_GR	0x80000000
+#define SDCPWR_GIZTIME_LONGLOCK	0x000000ff
+
+/* This should eventually come out of the device tree */
+#define SDCPWR_BASE		0xfc104000
+#define SDCPWR_SIZE		0x1000
+
+#define SDCASR_BASE		0xfc120000
+#define SDCASR_SIZE		0x2000
+
+static void __iomem *sdcpwr_mapbase;
+static void __iomem *sdcasr_mapbase;
+
+
+static DEFINE_MUTEX(pas_switch_mutex);
+
+/* Current astate, is used when waking up from power savings on
+ * one core, in case the other core has switched states during
+ * the idle time.
+ */
+static int current_astate;
+
+/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
+static struct cpufreq_frequency_table pas_freqs[] = {
+	{0,	0},
+	{1,	0},
+	{2,	0},
+	{3,	0},
+	{4,	0},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr *pas_cpu_freqs_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+/*
+ * hardware specific functions
+ */
+
+static int get_astate_freq(int astate)
+{
+	u32 ret;
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)) & 0x3f;
+
+	return ret;
+}
+
+static int get_cur_astate(int cpu)
+{
+	u32 ret;
+
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
+	ret = (ret >> (cpu * 4)) & 0x7;
+
+	return ret;
+}
+
+static int get_gizmo_latency(void)
+{
+	u32 giztime, ret;
+	giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
+
+	/* just provide the upper bound */
+	ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * ((giztime & SDCPWR_GIZTIME_GR) ? 128 : 1) * 1000;
+	return ret;
+}
+
+static void set_astate(int cpu, unsigned int astate)
+{
+	u64 flags;
+
+	/* Return if called before init has run */
+	if (unlikely(!sdcasr_mapbase))
+		return;
+
+	local_irq_save(flags);
+
+	out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
+
+	local_irq_restore(flags);
+}
+
+void restore_astate(int cpu)
+{
+	set_astate(cpu, current_astate);
+}
+
+/*
+ * cpufreq functions
+ */
+
+static int pas_cpufreq_cpu_init (struct cpufreq_policy *policy)
+{
+	u32 *max_freq;
+	int i, cur_astate;
+	struct device_node *cpu;
+
+	cpu = of_get_cpu_node(policy->cpu, NULL);
+
+	if(!cpu)
+		return -ENODEV;
+
+	sdcpwr_mapbase = ioremap(SDCPWR_BASE, SDCPWR_SIZE);
+	sdcasr_mapbase = ioremap(SDCASR_BASE, SDCASR_SIZE);
+	if (!sdcpwr_mapbase || !sdcasr_mapbase)
+		panic("SDCMAP: Cannot map registers!");
+
+	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
+
+	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
+
+	if(!max_freq)
+		return -EINVAL;
+
+	// we need the freq in kHz
+	*max_freq /= 1000;
+
+	pr_debug("max clock-frequency is at %u kHz\n", *max_freq);
+	pr_debug("initializing frequency table\n");
+
+	// initialize frequency table
+	for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
+		pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
+		pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+	}
+
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+	policy->cpuinfo.transition_latency = get_gizmo_latency();
+
+	cur_astate = get_cur_astate(policy->cpu);
+	pr_debug("current astate is at %d\n",cur_astate);
+
+	policy->cur = pas_freqs[cur_astate].frequency;
+	policy->cpus = cpu_possible_map;
+
+	cpufreq_frequency_table_get_attr (pas_freqs, policy->cpu);
+
+	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */
+	return cpufreq_frequency_table_cpuinfo (policy, pas_freqs);
+}
+
+static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static int pas_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, pas_freqs);
+}
+
+static int pas_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq,
+			    unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	int pas_astate_new;
+	int i;
+
+	cpufreq_frequency_table_target(policy,
+				       pas_freqs,
+				       target_freq,
+				       relation,
+				       &pas_astate_new);
+
+	freqs.old = policy->cur;
+	freqs.new = pas_freqs[pas_astate_new].frequency;
+	freqs.cpu = policy->cpu;
+
+	mutex_lock (&pas_switch_mutex);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
+		 policy->cpu,
+		 pas_freqs[pas_astate_new].frequency,
+		 pas_freqs[pas_astate_new].index);
+
+	current_astate = pas_astate_new;
+
+	for_each_online_cpu(i) {
+		set_astate(i, pas_astate_new);
+	}
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	mutex_unlock(&pas_switch_mutex);
+
+	return 0;
+}
+
+static struct cpufreq_driver pas_cpufreq_driver = {
+	.name		= "pas-cpufreq",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= pas_cpufreq_cpu_init,
+	.exit		= pas_cpufreq_cpu_exit,
+	.verify		= pas_cpufreq_verify,
+	.target		= pas_cpufreq_target,
+	.attr		= pas_cpu_freqs_attr,
+};
+
+/*
+ * module init and destoy
+ */
+
+static int __init pas_cpufreq_init(void)
+{
+	return cpufreq_register_driver(&pas_cpufreq_driver);
+}
+
+static void __exit pas_cpufreq_exit(void)
+{
+	cpufreq_unregister_driver(&pas_cpufreq_driver);
+}
+
+module_init(pas_cpufreq_init);
+module_exit(pas_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
Index: powerpc/arch/powerpc/platforms/pasemi/idle.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/idle.c
+++ powerpc/arch/powerpc/platforms/pasemi/idle.c
@@ -61,6 +61,10 @@ static int pasemi_system_reset_exception
 		/* do system reset */
 		return 0;
 	}
+
+	/* Set higher astate since we come out of power savings at 0 */
+	restore_astate(hard_smp_processor_id());
+
 	/* everything handled */
 	regs->msr |= MSR_RI;
 	return 1;
@@ -68,6 +72,11 @@ static int pasemi_system_reset_exception
 
 void __init pasemi_idle_init(void)
 {
+#ifndef CONFIG_PPC_PASEMI_CPUFREQ
+	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
+	current_mode = 0;
+#endif
+
 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
 	ppc_md.power_save = modes[current_mode].entry;
 	printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
Index: powerpc/arch/powerpc/platforms/pasemi/pasemi.h
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/pasemi.h
+++ powerpc/arch/powerpc/platforms/pasemi/pasemi.h
@@ -14,6 +14,14 @@ extern void __init pasemi_idle_init(void
 extern void idle_spin(void);
 extern void idle_doze(void);
 
+/* Restore astate to last set */
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+extern void restore_astate(int cpu);
+#else
+static inline void restore_astate(int cpu)
+{
+}
+#endif
 
 
 #endif /* _PASEMI_PASEMI_H */
Index: powerpc/arch/powerpc/platforms/Kconfig
===================================================================
--- powerpc.orig/arch/powerpc/platforms/Kconfig
+++ powerpc/arch/powerpc/platforms/Kconfig
@@ -155,7 +155,7 @@ source "drivers/cpufreq/Kconfig"
 
 config CPU_FREQ_PMAC
 	bool "Support for Apple PowerBooks"
-	depends on CPU_FREQ && ADB_PMU && PPC32
+	depends on ADB_PMU && PPC32
 	select CPU_FREQ_TABLE
 	help
 	  This adds support for frequency switching on Apple PowerBooks,
@@ -164,11 +164,21 @@ config CPU_FREQ_PMAC
 
 config CPU_FREQ_PMAC64
 	bool "Support for some Apple G5s"
-	depends on CPU_FREQ && PPC64
+	depends on PPC64
 	select CPU_FREQ_TABLE
 	help
 	  This adds support for frequency switching on Apple iMac G5,
 	  and some of the more recent desktop G5 machines as well.
+
+config PPC_PASEMI_CPUFREQ
+	bool "Support for PA Semi PWRficient"
+	depends on PPC_PASEMI
+	default y
+	select CPU_FREQ_TABLE
+	help
+	  This adds the support for frequency switching on PA Semi
+	  PWRficient processors.
+
 endmenu
 
 config PPC601_SYNC_FIX

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] [2.6.22] pasemi: cpufreq driver
  2007-04-25 20:46 [PATCH] [2.6.22] pasemi: cpufreq driver Olof Johansson
@ 2007-04-25 23:47 ` Arnd Bergmann
  2007-04-25 23:57   ` Olof Johansson
  2007-04-26  6:56   ` cbe_cpufreq crashes my machine Olof Johansson
  2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
  1 sibling, 2 replies; 29+ messages in thread
From: Arnd Bergmann @ 2007-04-25 23:47 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Olof Johansson, egor, paulus, cpufreq

On Wednesday 25 April 2007, Olof Johansson wrote:

> +/* This should eventually come out of the device tree */
> +#define SDCPWR_BASE		0xfc104000
> +#define SDCPWR_SIZE		0x1000
> +
> +#define SDCASR_BASE		0xfc120000
> +#define SDCASR_SIZE		0x2000

the comment is right. Why don't you do it then? ;-)

> +static int pas_cpufreq_cpu_init (struct cpufreq_policy *policy)
> +{
> +	u32 *max_freq;
> +	int i, cur_astate;
> +	struct device_node *cpu;
> +
> +	cpu = of_get_cpu_node(policy->cpu, NULL);
> +
> +	if(!cpu)
> +		return -ENODEV;
> +
> +	sdcpwr_mapbase = ioremap(SDCPWR_BASE, SDCPWR_SIZE);
> +	sdcasr_mapbase = ioremap(SDCASR_BASE, SDCASR_SIZE);
> +	if (!sdcpwr_mapbase || !sdcasr_mapbase)
> +		panic("SDCMAP: Cannot map registers!");

I can't see any check in here that finds out if you are actually running on
the right hardware. 
The proper way to implement this driver would be to register an
of_platform_driver for the cpufreq device and then get the register
addresses from there,

> @@ -155,7 +155,7 @@ source "drivers/cpufreq/Kconfig"
>  
>  config CPU_FREQ_PMAC
>  	bool "Support for Apple PowerBooks"
> -	depends on CPU_FREQ && ADB_PMU && PPC32
> +	depends on ADB_PMU && PPC32
>  	select CPU_FREQ_TABLE
>  	help
>  	  This adds support for frequency switching on Apple PowerBooks,
> @@ -164,11 +164,21 @@ config CPU_FREQ_PMAC
>  
>  config CPU_FREQ_PMAC64
>  	bool "Support for some Apple G5s"
> -	depends on CPU_FREQ && PPC64
> +	depends on PPC64
>  	select CPU_FREQ_TABLE
>  	help
>  	  This adds support for frequency switching on Apple iMac G5,
>  	  and some of the more recent desktop G5 machines as well.

Why this change?

> +config PPC_PASEMI_CPUFREQ
> +	bool "Support for PA Semi PWRficient"
> +	depends on PPC_PASEMI
> +	default y
> +	select CPU_FREQ_TABLE
> +	help
> +	  This adds the support for frequency switching on PA Semi
> +	  PWRficient processors.
> +
>  endmenu

Why bool and not tristate?

	Arnd <><

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] [2.6.22] pasemi: cpufreq driver
  2007-04-25 23:47 ` Arnd Bergmann
@ 2007-04-25 23:57   ` Olof Johansson
  2007-04-26  1:57     ` Olof Johansson
  2007-04-26  6:56   ` cbe_cpufreq crashes my machine Olof Johansson
  1 sibling, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-25 23:57 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Thu, Apr 26, 2007 at 01:47:35AM +0200, Arnd Bergmann wrote:
> On Wednesday 25 April 2007, Olof Johansson wrote:
> 
> > +/* This should eventually come out of the device tree */
> > +#define SDCPWR_BASE		0xfc104000
> > +#define SDCPWR_SIZE		0x1000
> > +
> > +#define SDCASR_BASE		0xfc120000
> > +#define SDCASR_SIZE		0x2000
> 
> the comment is right. Why don't you do it then? ;-)

Leftovers from when it was a regular platform driver.

> > +static int pas_cpufreq_cpu_init (struct cpufreq_policy *policy)
> > +{
> > +	u32 *max_freq;
> > +	int i, cur_astate;
> > +	struct device_node *cpu;
> > +
> > +	cpu = of_get_cpu_node(policy->cpu, NULL);
> > +
> > +	if(!cpu)
> > +		return -ENODEV;
> > +
> > +	sdcpwr_mapbase = ioremap(SDCPWR_BASE, SDCPWR_SIZE);
> > +	sdcasr_mapbase = ioremap(SDCASR_BASE, SDCASR_SIZE);
> > +	if (!sdcpwr_mapbase || !sdcasr_mapbase)
> > +		panic("SDCMAP: Cannot map registers!");
> 
> I can't see any check in here that finds out if you are actually running on
> the right hardware. 
> The proper way to implement this driver would be to register an
> of_platform_driver for the cpufreq device and then get the register
> addresses from there,

Yup.

> > @@ -155,7 +155,7 @@ source "drivers/cpufreq/Kconfig"
> >  
> >  config CPU_FREQ_PMAC
> >  	bool "Support for Apple PowerBooks"
> > -	depends on CPU_FREQ && ADB_PMU && PPC32
> > +	depends on ADB_PMU && PPC32
> >  	select CPU_FREQ_TABLE
> >  	help
> >  	  This adds support for frequency switching on Apple PowerBooks,
> > @@ -164,11 +164,21 @@ config CPU_FREQ_PMAC
> >  
> >  config CPU_FREQ_PMAC64
> >  	bool "Support for some Apple G5s"
> > -	depends on CPU_FREQ && PPC64
> > +	depends on PPC64
> >  	select CPU_FREQ_TABLE
> >  	help
> >  	  This adds support for frequency switching on Apple iMac G5,
> >  	  and some of the more recent desktop G5 machines as well.
> 
> Why this change?

They're redundant, but they should have been in a separate patch. I
forgot to revert them before rediffing.

> > +config PPC_PASEMI_CPUFREQ
> > +	bool "Support for PA Semi PWRficient"
> > +	depends on PPC_PASEMI
> > +	default y
> > +	select CPU_FREQ_TABLE
> > +	help
> > +	  This adds the support for frequency switching on PA Semi
> > +	  PWRficient processors.
> > +
> >  endmenu
> 
> Why bool and not tristate?

1) The other cpufreq drivers are bool
2) See the idle loop interaction: It can go tristate once we have runtime
selection of idle loop, but until then we need it for idle=doze.


-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] [2.6.22] pasemi: cpufreq driver
  2007-04-25 23:57   ` Olof Johansson
@ 2007-04-26  1:57     ` Olof Johansson
  0 siblings, 0 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-26  1:57 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Wed, Apr 25, 2007 at 06:57:57PM -0500, olof wrote:

> 2) See the idle loop interaction: It can go tristate once we have runtime
> selection of idle loop, but until then we need it for idle=doze.

Grmbl. Looks like I posted a stale patch that actually won't build with
PPC_PASEMI_CPUFREQ=n. I'll repost with the correct version.


-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-25 20:46 [PATCH] [2.6.22] pasemi: cpufreq driver Olof Johansson
  2007-04-25 23:47 ` Arnd Bergmann
@ 2007-04-26  5:37 ` Olof Johansson
  2007-04-26  8:55   ` Arnd Bergmann
                     ` (2 more replies)
  1 sibling, 3 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-26  5:37 UTC (permalink / raw)
  To: paulus; +Cc: linuxppc-dev, egor, arnd, cpufreq

Cpufreq driver for PA Semi PWRficient processors.

Signed-off-by: Egor Martovetsky <egor@pasemi.com>
Signed-off-by: Olof Johansson <olof@lixom.net>

---

Changes since last version:
* Attributed copyright correctly to cbe_cpufreq.c and adjust license
  to match (this was my mistake)
* Lookup the SDC and Gizmo device nodes to get register bases
* machine_is_compatible checks
* Cleanups as suggested by Arnd + misc whitespace
* Rebased on top of MDIO config patch

I chose not to do this as an of_platform driver since it doesn't fit
that well with the cpufreq driver model; having 3 levels of init/probe
functions is excessive.

Index: powerpc/arch/powerpc/platforms/pasemi/Makefile
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/Makefile
+++ powerpc/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,3 @@
 obj-y	+= setup.o pci.o time.o idle.o powersave.o iommu.o
 obj-$(CONFIG_PPC_PASEMI_MDIO)	+= gpio_mdio.o
+obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o
Index: powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
===================================================================
--- /dev/null
+++ powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Authors: Egor Martovetsky <egor@pasemi.com>
+ *	    Olof Johansson <olof@lixom.net>
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/timer.h>
+
+#include <asm/hw_irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#define SDCASR_REG		0x0100
+#define SDCASR_REG_STRIDE	0x1000
+#define SDCPWR_CFGA0_REG	0x0100
+#define SDCPWR_PWST0_REG	0x0000
+#define SDCPWR_GIZTIME_REG	0x0440
+
+/* SDCPWR_GIZTIME_REG fields */
+#define SDCPWR_GIZTIME_GR	0x80000000
+#define SDCPWR_GIZTIME_LONGLOCK	0x000000ff
+
+/* Offset of ASR registers from SDC base */
+#define SDCASR_OFFSET		0x120000
+
+static void __iomem *sdcpwr_mapbase;
+static void __iomem *sdcasr_mapbase;
+
+static DEFINE_MUTEX(pas_switch_mutex);
+
+/* Current astate, is used when waking up from power savings on
+ * one core, in case the other core has switched states during
+ * the idle time.
+ */
+static int current_astate;
+
+/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
+static struct cpufreq_frequency_table pas_freqs[] = {
+	{0,	0},
+	{1,	0},
+	{2,	0},
+	{3,	0},
+	{4,	0},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr *pas_cpu_freqs_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+/*
+ * hardware specific functions
+ */
+
+static int get_astate_freq(int astate)
+{
+	u32 ret;
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
+
+	return ret & 0x3f;
+}
+
+static int get_cur_astate(int cpu)
+{
+	u32 ret;
+
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
+	ret = (ret >> (cpu * 4)) & 0x7;
+
+	return ret;
+}
+
+static int get_gizmo_latency(void)
+{
+	u32 giztime, ret;
+
+	giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
+
+	/* just provide the upper bound */
+	if (giztime & SDCPWR_GIZTIME_GR)
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
+	else
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
+
+	return ret;
+}
+
+static void set_astate(int cpu, unsigned int astate)
+{
+	u64 flags;
+
+	/* Return if called before init has run */
+	if (unlikely(!sdcasr_mapbase))
+		return;
+
+	local_irq_save(flags);
+
+	out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
+
+	local_irq_restore(flags);
+}
+
+void restore_astate(int cpu)
+{
+	set_astate(cpu, current_astate);
+}
+
+/*
+ * cpufreq functions
+ */
+
+static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	u32 *max_freq;
+	int i, cur_astate;
+	struct resource res;
+	struct device_node *cpu, *dn;
+	int err = -ENODEV;
+
+	cpu = of_get_cpu_node(policy->cpu, NULL);
+
+	if (!cpu)
+		goto out;
+
+	dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
+	if (!dn)
+		goto out;
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out;
+	sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
+	if (!sdcasr_mapbase) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
+	if (!dn) {
+		err = -ENODEV;
+		goto out_unmap_sdcasr;
+	}
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out_unmap_sdcasr;
+	sdcpwr_mapbase = ioremap(res.start, 0x1000);
+	if (!sdcpwr_mapbase) {
+		err = -EINVAL;
+		goto out_unmap_sdcasr;
+	}
+
+	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
+
+	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
+	if (!max_freq) {
+		err = -EINVAL;
+		goto out_unmap_sdcpwr;
+	}
+
+	/* we need the freq in kHz */
+	*max_freq /= 1000;
+
+	pr_debug("max clock-frequency is at %u kHz\n", *max_freq);
+	pr_debug("initializing frequency table\n");
+
+	/* initialize frequency table */
+	for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
+		pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
+		pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+	}
+
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+	policy->cpuinfo.transition_latency = get_gizmo_latency();
+
+	cur_astate = get_cur_astate(policy->cpu);
+	pr_debug("current astate is at %d\n",cur_astate);
+
+	policy->cur = pas_freqs[cur_astate].frequency;
+	policy->cpus = cpu_possible_map;
+
+	cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
+
+	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
+	 * are set correctly
+	 */
+	return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
+
+out_unmap_sdcpwr:
+	iounmap(sdcpwr_mapbase);
+
+out_unmap_sdcasr:
+	iounmap(sdcasr_mapbase);
+out:
+	return err;
+}
+
+static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+	if (sdcasr_mapbase)
+		iounmap(sdcasr_mapbase);
+	if (sdcpwr_mapbase)
+		iounmap(sdcpwr_mapbase);
+
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static int pas_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, pas_freqs);
+}
+
+static int pas_cpufreq_target(struct cpufreq_policy *policy,
+			      unsigned int target_freq,
+			      unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	int pas_astate_new;
+	int i;
+
+	cpufreq_frequency_table_target(policy,
+				       pas_freqs,
+				       target_freq,
+				       relation,
+				       &pas_astate_new);
+
+	freqs.old = policy->cur;
+	freqs.new = pas_freqs[pas_astate_new].frequency;
+	freqs.cpu = policy->cpu;
+
+	mutex_lock(&pas_switch_mutex);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
+		 policy->cpu,
+		 pas_freqs[pas_astate_new].frequency,
+		 pas_freqs[pas_astate_new].index);
+
+	current_astate = pas_astate_new;
+
+	for_each_online_cpu(i)
+		set_astate(i, pas_astate_new);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	mutex_unlock(&pas_switch_mutex);
+
+	return 0;
+}
+
+static struct cpufreq_driver pas_cpufreq_driver = {
+	.name		= "pas-cpufreq",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= pas_cpufreq_cpu_init,
+	.exit		= pas_cpufreq_cpu_exit,
+	.verify		= pas_cpufreq_verify,
+	.target		= pas_cpufreq_target,
+	.attr		= pas_cpu_freqs_attr,
+};
+
+/*
+ * module init and destoy
+ */
+
+static int __init pas_cpufreq_init(void)
+{
+	if (!machine_is_compatible("PA6T-1682M"))
+		return 0;
+
+	return cpufreq_register_driver(&pas_cpufreq_driver);
+}
+
+static void __exit pas_cpufreq_exit(void)
+{
+	cpufreq_unregister_driver(&pas_cpufreq_driver);
+}
+
+module_init(pas_cpufreq_init);
+module_exit(pas_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
Index: powerpc/arch/powerpc/platforms/pasemi/idle.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/idle.c
+++ powerpc/arch/powerpc/platforms/pasemi/idle.c
@@ -61,6 +61,10 @@ static int pasemi_system_reset_exception
 		/* do system reset */
 		return 0;
 	}
+
+	/* Set higher astate since we come out of power savings at 0 */
+	restore_astate(hard_smp_processor_id());
+
 	/* everything handled */
 	regs->msr |= MSR_RI;
 	return 1;
@@ -68,6 +72,11 @@ static int pasemi_system_reset_exception
 
 void __init pasemi_idle_init(void)
 {
+#ifndef CONFIG_PPC_PASEMI_CPUFREQ
+	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
+	current_mode = 0;
+#endif
+
 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
 	ppc_md.power_save = modes[current_mode].entry;
 	printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
Index: powerpc/arch/powerpc/platforms/pasemi/pasemi.h
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/pasemi.h
+++ powerpc/arch/powerpc/platforms/pasemi/pasemi.h
@@ -14,6 +14,14 @@ extern void __init pasemi_idle_init(void
 extern void idle_spin(void);
 extern void idle_doze(void);
 
+/* Restore astate to last set */
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+extern void restore_astate(int cpu);
+#else
+static inline void restore_astate(int cpu)
+{
+}
+#endif
 
 
 #endif /* _PASEMI_PASEMI_H */
Index: powerpc/arch/powerpc/platforms/Kconfig
===================================================================
--- powerpc.orig/arch/powerpc/platforms/Kconfig
+++ powerpc/arch/powerpc/platforms/Kconfig
@@ -169,6 +169,16 @@ config CPU_FREQ_PMAC64
 	help
 	  This adds support for frequency switching on Apple iMac G5,
 	  and some of the more recent desktop G5 machines as well.
+
+config PPC_PASEMI_CPUFREQ
+	bool "Support for PA Semi PWRficient"
+	depends on PPC_PASEMI
+	default y
+	select CPU_FREQ_TABLE
+	help
+	  This adds the support for frequency switching on PA Semi
+	  PWRficient processors.
+
 endmenu
 
 config PPC601_SYNC_FIX

^ permalink raw reply	[flat|nested] 29+ messages in thread

* cbe_cpufreq crashes my machine
  2007-04-25 23:47 ` Arnd Bergmann
  2007-04-25 23:57   ` Olof Johansson
@ 2007-04-26  6:56   ` Olof Johansson
  2007-04-26  8:39     ` Benjamin Herrenschmidt
  2007-04-26 23:07     ` [PATCH] cell: cbe_cpufreq cleanup and crash fix Olof Johansson
  1 sibling, 2 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-26  6:56 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, Christian Krafft

On Thu, Apr 26, 2007 at 01:47:35AM +0200, Arnd Bergmann wrote:
> I can't see any check in here that finds out if you are actually running on
> the right hardware. 

Seems like this came along with our use of cbe_cpufreq.c as a basis. You
don't do it either! :-)

Enabling CONFIG_CBE_CPUFREQ makes my machine die a horrible death,
see below. I'd post a patch if I knew what machine compatible fields to
compare with, but I have no clue what's considered approprate to check
for on cell.


Unable to handle kernel paging request for data at address 0x00000888
Faulting instruction address: 0xc000000000036b14
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=2
Modules linked in:
NIP: C000000000036B14 LR: C000000000036B08 CTR: C000000000036A60
REGS: c00000007fd83830 TRAP: 0300   Not tainted  (2.6.20)
MSR: 9000000000009032 <EE,ME,IR,DR>  CR: 82000022  XER: 20000000
DAR: 0000000000000888, DSISR: 0000000040000000
TASK = c00000000ffc5820[1] 'swapper' THREAD: c00000007fd80000 CPU: 0
GPR00: C00000000093F940 C00000007FD83AB0 C000000000916EC0 0000000000000000
GPR04: C00000000068343F C0000000009C1208 C00000007F8FAE00 0000000000000000
GPR08: C000000000859720 0000000000000888 C0000000009C3DE0 0000000000000000
GPR12: 0000000000000000 C000000000779600 0000000000000000 C000000000684808
GPR16: 4000000000000000 C0000000006834E0 0000000000000000 0000000000000000
GPR20: 0000000000764880 C000000000764880 C000000000764AF0 0000000000764AF0
GPR24: C000000000684308 C00000000085FAE8 C0000000025DD028 0000000000000000
GPR28: FFFFFFFFFFFFFFF4 C00000007F8FAC38 C000000000784B58 C00000007F8FAC00
NIP [C000000000036B14] .cbe_cpufreq_cpu_init+0xb4/0x12c
LR [C000000000036B08] .cbe_cpufreq_cpu_init+0xa8/0x12c
Call Trace:
[C00000007FD83AB0] [C000000000036AA8] .cbe_cpufreq_cpu_init+0x48/0x12c (unreliable)
[C00000007FD83B40] [C0000000004831FC] .cpufreq_add_dev+0x158/0x4f4
[C00000007FD83D00] [C00000000033A5E4] .sysdev_driver_register+0xbc/0x158
[C00000007FD83D90] [C000000000482FF0] .cpufreq_register_driver+0xd0/0x184
[C00000007FD83E40] [C0000000007421B4] .cbe_cpufreq_init+0x1c/0x34
[C00000007FD83EC0] [C0000000000093B4] .init+0x1c4/0x39c
[C00000007FD83F90] [C000000000022E10] .kernel_thread+0x4c/0x68
Instruction dump:
800b0000 2f80fffe 409effe4 e93e8038 380061a8 e87f000e 901f0018 f93f0030
4bffe239 60000000 39230888 7c0004ac <e9230888> 0c090000 4c00012c ebbe8008



-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: cbe_cpufreq crashes my machine
  2007-04-26  6:56   ` cbe_cpufreq crashes my machine Olof Johansson
@ 2007-04-26  8:39     ` Benjamin Herrenschmidt
  2007-04-26 23:07     ` [PATCH] cell: cbe_cpufreq cleanup and crash fix Olof Johansson
  1 sibling, 0 replies; 29+ messages in thread
From: Benjamin Herrenschmidt @ 2007-04-26  8:39 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, Arnd Bergmann, Christian Krafft

On Thu, 2007-04-26 at 01:56 -0500, Olof Johansson wrote:
> On Thu, Apr 26, 2007 at 01:47:35AM +0200, Arnd Bergmann wrote:
> > I can't see any check in here that finds out if you are actually running on
> > the right hardware. 
> 
> Seems like this came along with our use of cbe_cpufreq.c as a basis. You
> don't do it either! :-)
> 
> Enabling CONFIG_CBE_CPUFREQ makes my machine die a horrible death,
> see below. I'd post a patch if I knew what machine compatible fields to
> compare with, but I have no clue what's considered approprate to check
> for on cell.

Or machine_is(cell)

Ben.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
@ 2007-04-26  8:55   ` Arnd Bergmann
  2007-04-26 16:48     ` Olof Johansson
  2007-04-26 10:26   ` Johannes Berg
  2007-04-27  5:46   ` [PATCH v3] " Olof Johansson
  2 siblings, 1 reply; 29+ messages in thread
From: Arnd Bergmann @ 2007-04-26  8:55 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Olof Johansson, egor, paulus, cpufreq

On Thursday 26 April 2007, Olof Johansson wrote:

> I chose not to do this as an of_platform driver since it doesn't fit
> that well with the cpufreq driver model; having 3 levels of init/probe
> functions is excessive.

<snip>

> +	dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
> +	if (!dn)
> +		goto out;
> +	err = of_address_to_resource(dn, 0, &res);
> +	of_node_put(dn);
> +	if (err)
> +		goto out;
> +	sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
> +	if (!sdcasr_mapbase) {
> +		err = -EINVAL;
> +		goto out;
> +	}
> +
> +	dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
> +	if (!dn) {
> +		err = -ENODEV;
> +		goto out_unmap_sdcasr;
> +	}
> +	err = of_address_to_resource(dn, 0, &res);
> +	of_node_put(dn);
> +	if (err)
> +		goto out_unmap_sdcasr;
> +	sdcpwr_mapbase = ioremap(res.start, 0x1000);
> +	if (!sdcpwr_mapbase) {
> +		err = -EINVAL;
> +		goto out_unmap_sdcasr;
> +	}

What are sdc and gizmo anyway? If they are both only used for cpufreq, maybe the
easiest way to do this with an of_platform_driver would be to have a single
node that has two register ranges.

	Arnd <><

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
  2007-04-26  8:55   ` Arnd Bergmann
@ 2007-04-26 10:26   ` Johannes Berg
  2007-04-26 20:37     ` Olof Johansson
  2007-04-27  5:46   ` [PATCH v3] " Olof Johansson
  2 siblings, 1 reply; 29+ messages in thread
From: Johannes Berg @ 2007-04-26 10:26 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, egor, paulus, arnd, cpufreq

[-- Attachment #1: Type: text/plain, Size: 942 bytes --]

On Thu, 2007-04-26 at 00:37 -0500, Olof Johansson wrote:
> 
> +       policy->cur = pas_freqs[cur_astate].frequency;
> +       policy->cpus = cpu_possible_map; 

That doesn't seem right.

Either, all your processors scale along each other in which case you
should use cpu_online_map here, or they scale each on their own in which
case you just set a single bit here.

The generic code works like this:
CPU 0 is brought online and cpufreq initialised for it
CPU 1 is brought online and cpufreq initialised for it. if cpus includes
more than a single bit, cpufreq is linked to the first other CPU in
policy->cpus and then cpufreq for CPU1 is deinitialised again.

We have the same bug on powermac but for some reason the patch to fix it
that I posted a long time ago (look for "powermac: fix G5-cpufreq for
cpu on/offline") wasn't applied yet. Look at it though, it includes a
comment on what needs to be done.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26  8:55   ` Arnd Bergmann
@ 2007-04-26 16:48     ` Olof Johansson
  2007-04-26 17:11       ` Arnd Bergmann
  0 siblings, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-26 16:48 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Thu, Apr 26, 2007 at 10:55:33AM +0200, Arnd Bergmann wrote:
> On Thursday 26 April 2007, Olof Johansson wrote:
> 
> > I chose not to do this as an of_platform driver since it doesn't fit
> > that well with the cpufreq driver model; having 3 levels of init/probe
> > functions is excessive.
> 
> <snip>
> 
> > +	dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
> > +	if (!dn)
> > +		goto out;
> > +	err = of_address_to_resource(dn, 0, &res);
> > +	of_node_put(dn);
> > +	if (err)
> > +		goto out;
> > +	sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
> > +	if (!sdcasr_mapbase) {
> > +		err = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
> > +	if (!dn) {
> > +		err = -ENODEV;
> > +		goto out_unmap_sdcasr;
> > +	}
> > +	err = of_address_to_resource(dn, 0, &res);
> > +	of_node_put(dn);
> > +	if (err)
> > +		goto out_unmap_sdcasr;
> > +	sdcpwr_mapbase = ioremap(res.start, 0x1000);
> > +	if (!sdcpwr_mapbase) {
> > +		err = -EINVAL;
> > +		goto out_unmap_sdcasr;
> > +	}
> 
> What are sdc and gizmo anyway? If they are both only used for cpufreq, maybe the
> easiest way to do this with an of_platform_driver would be to have a single
> node that has two register ranges.

SDC is the system and debug controller, it contains a number of smaller
devices such as the PIC, the PMU (Gizmo), RNG, and various debug
features. Some already have drivers submitted, others will later on.

Unfortunately the setting of the current active state is done to an SDC
register, while information of the states is in the PMU, so access to
both is needed in the driver.

That doesn't change the fact that making this an of_platform driver
is excessive:

* module_init to register an of_platform driver
* of_platform walks the tree, calls probes
* of_platform driver probe code to register the cpufreq driver
* cpufreq calls it's registered drivers
* the cpufreq driver in turn will do the inits

Compare to:
* module_init registers cpufreq driver if machine_is_compatible()
* cpufreq calls it's registered drivers
* the cpufreq driver in turn will do the inits

Don't get me wrong, of_platform drivers are often convenient, but I
don't see it being a benefit to use them in this case.


-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 16:48     ` Olof Johansson
@ 2007-04-26 17:11       ` Arnd Bergmann
  2007-04-26 19:05         ` Segher Boessenkool
  2007-04-26 20:26         ` Olof Johansson
  0 siblings, 2 replies; 29+ messages in thread
From: Arnd Bergmann @ 2007-04-26 17:11 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Thursday 26 April 2007, Olof Johansson wrote:
> SDC is the system and debug controller, it contains a number of smaller
> devices such as the PIC, the PMU (Gizmo), RNG, and various debug
> features. Some already have drivers submitted, others will later on.

Oh well, these chips all seem to be the same. On cell, we have solved
the problem by introducing the 'cbe_regs' helper library that gives
access to all those miscellaneous registers to the individual device
drivers, so that not all of them need to scan the device tree for
the same registers and map them individually.

The problem with an of_platform_driver for this would be that you
can only have _one_ driver attached to the registers.

> Unfortunately the setting of the current active state is done to an SDC
> register, while information of the states is in the PMU, so access to
> both is needed in the driver.

One thing that you could do is to list only this one register of the
SDC in the reg property, not all of the SDC. 
 
	Arnd <><

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 17:11       ` Arnd Bergmann
@ 2007-04-26 19:05         ` Segher Boessenkool
  2007-04-26 20:38           ` Olof Johansson
  2007-04-26 20:26         ` Olof Johansson
  1 sibling, 1 reply; 29+ messages in thread
From: Segher Boessenkool @ 2007-04-26 19:05 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: Olof Johansson, linuxppc-dev, egor, paulus, cpufreq

>> Unfortunately the setting of the current active state is done to an 
>> SDC
>> register, while information of the states is in the PMU, so access to
>> both is needed in the driver.
>
> One thing that you could do is to list only this one register of the
> SDC in the reg property, not all of the SDC.

The system-controller node should really describe
all of the system controller.  If the Linux driver
needs to access two separate devices, it should
really just do that.  Don't try to make things
"simpler" in the device tree.

You probably should map the SDC registers in the
platform startop code, and then have the various
platform drivers use that.  Maybe even do an
abstraction ;-)


Segher

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 17:11       ` Arnd Bergmann
  2007-04-26 19:05         ` Segher Boessenkool
@ 2007-04-26 20:26         ` Olof Johansson
  2007-04-26 20:43           ` Arnd Bergmann
  1 sibling, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-26 20:26 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Thu, Apr 26, 2007 at 07:11:21PM +0200, Arnd Bergmann wrote:
> On Thursday 26 April 2007, Olof Johansson wrote:
> > SDC is the system and debug controller, it contains a number of smaller
> > devices such as the PIC, the PMU (Gizmo), RNG, and various debug
> > features. Some already have drivers submitted, others will later on.
> 
> Oh well, these chips all seem to be the same. On cell, we have solved
> the problem by introducing the 'cbe_regs' helper library that gives
> access to all those miscellaneous registers to the individual device
> drivers, so that not all of them need to scan the device tree for
> the same registers and map them individually.

Not a bad idea, I'll consider it for the future. Most of our devices are
on a pseudo-PCI bus, so it's only a handful that need special register
access. The stuff in the SDC is the biggest exception.

> The problem with an of_platform_driver for this would be that you
> can only have _one_ driver attached to the registers.
>
> > Unfortunately the setting of the current active state is done to an SDC
> > register, while information of the states is in the PMU, so access to
> > both is needed in the driver.
> 
> One thing that you could do is to list only this one register of the
> SDC in the reg property, not all of the SDC. 

That doesn't make much sense in this case. The register isn't in the
Gizmo.

Having a driver/library for the SDC that the gizmo driver can use is a
good idea, similar to what you've done on cell. I'll try to have that
ready for 2.6.23, but that shouldn't stop this base driver from going
in now.


-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 10:26   ` Johannes Berg
@ 2007-04-26 20:37     ` Olof Johansson
  2007-04-27  9:40       ` Johannes Berg
  0 siblings, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-26 20:37 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, egor, paulus, arnd, cpufreq

On Thu, Apr 26, 2007 at 12:26:00PM +0200, Johannes Berg wrote:
> On Thu, 2007-04-26 at 00:37 -0500, Olof Johansson wrote:
> > 
> > +       policy->cur = pas_freqs[cur_astate].frequency;
> > +       policy->cpus = cpu_possible_map; 
> 
> That doesn't seem right.
> 
> Either, all your processors scale along each other in which case you
> should use cpu_online_map here, or they scale each on their own in which
> case you just set a single bit here.

You can set the requested speed per-processor, but the actual speed
will be the max of them. Because of this, it's less surprising to set
it on both at the same time since then you'll at least know what you're
running at.

So yes, I'll change it to online_map instead of possible_map.

> The generic code works like this:
> CPU 0 is brought online and cpufreq initialised for it
> CPU 1 is brought online and cpufreq initialised for it. if cpus includes
> more than a single bit, cpufreq is linked to the first other CPU in
> policy->cpus and then cpufreq for CPU1 is deinitialised again.
>
> We have the same bug on powermac but for some reason the patch to fix it
> that I posted a long time ago (look for "powermac: fix G5-cpufreq for
> cpu on/offline") wasn't applied yet. Look at it though, it includes a
> comment on what needs to be done.

Hmm, I just tried adding debugging to the cpu_init code, and I'm not
seeing it called more than once (i.e. only for cpu 0). Mind you, I don't
have cpu hotplug support at this time.






-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 19:05         ` Segher Boessenkool
@ 2007-04-26 20:38           ` Olof Johansson
  2007-04-27  0:10             ` Segher Boessenkool
  0 siblings, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-26 20:38 UTC (permalink / raw)
  To: Segher Boessenkool; +Cc: linuxppc-dev, paulus, egor, Arnd Bergmann, cpufreq

On Thu, Apr 26, 2007 at 09:05:59PM +0200, Segher Boessenkool wrote:
> >>Unfortunately the setting of the current active state is done to an 
> >>SDC
> >>register, while information of the states is in the PMU, so access to
> >>both is needed in the driver.
> >
> >One thing that you could do is to list only this one register of the
> >SDC in the reg property, not all of the SDC.
> 
> The system-controller node should really describe
> all of the system controller.  If the Linux driver
> needs to access two separate devices, it should
> really just do that.  Don't try to make things
> "simpler" in the device tree.
> 
> You probably should map the SDC registers in the
> platform startop code, and then have the various
> platform drivers use that.  Maybe even do an
> abstraction ;-)

Yep, it's a good idea to do and I'll look at it for 2.6.23 time frame.


Thanks,

-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 20:26         ` Olof Johansson
@ 2007-04-26 20:43           ` Arnd Bergmann
  0 siblings, 0 replies; 29+ messages in thread
From: Arnd Bergmann @ 2007-04-26 20:43 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, egor, paulus, cpufreq

On Thursday 26 April 2007, Olof Johansson wrote:
> Having a driver/library for the SDC that the gizmo driver can use is a
> good idea, similar to what you've done on cell. I'll try to have that
> ready for 2.6.23, but that shouldn't stop this base driver from going
> in now.

Yes, agreed.

	Arnd <><

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] cell: cbe_cpufreq cleanup and crash fix
  2007-04-26  6:56   ` cbe_cpufreq crashes my machine Olof Johansson
  2007-04-26  8:39     ` Benjamin Herrenschmidt
@ 2007-04-26 23:07     ` Olof Johansson
  2007-04-27  5:22       ` [Cbe-oss-dev] " Akinobu Mita
  2007-04-27  5:33       ` [PATCH v2] " Olof Johansson
  1 sibling, 2 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-26 23:07 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, cbe-oss-dev, Christian Krafft

cbe_cpufreq cleanups:

* comment format
* whitespace
* don't init on non-cell platforms

Signed-off-by: Olof Johansson <olof@lixom.net>

Index: powerpc/arch/powerpc/platforms/cell/cbe_cpufreq.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/cell/cbe_cpufreq.c
+++ powerpc/arch/powerpc/platforms/cell/cbe_cpufreq.c
@@ -155,7 +155,7 @@ static int set_pmode_reg(int cpu, unsign
 }
 
 static int set_pmode(int cpu, unsigned int slow_mode) {
-	if(pmi_dev)
+	if (pmi_dev)
 		return set_pmode_pmi(cpu, slow_mode);
 	else
 		return set_pmode_reg(cpu, slow_mode);
@@ -167,7 +167,7 @@ static void cbe_cpufreq_handle_pmi(struc
 	u8 cpu;
 	u8 cbe_pmode_new;
 
-	BUG_ON (pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
+	BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
 
 	cpu = cbe_node_to_cpu(pmi_msg.data1);
 	cbe_pmode_new = pmi_msg.data2;
@@ -191,7 +191,7 @@ static struct pmi_handler cbe_pmi_handle
  * cpufreq functions
  */
 
-static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy)
+static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	const u32 *max_freqp;
 	u32 max_freq;
@@ -200,7 +200,7 @@ static int cbe_cpufreq_cpu_init (struct 
 
 	cpu = of_get_cpu_node(policy->cpu, NULL);
 
-	if(!cpu)
+	if (!cpu)
 		return -ENODEV;
 
 	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
@@ -210,13 +210,13 @@ static int cbe_cpufreq_cpu_init (struct 
 	if (!max_freqp)
 		return -EINVAL;
 
-	// we need the freq in kHz
+	/* we need the freq in kHz */
 	max_freq = *max_freqp / 1000;
 
 	pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 	pr_debug("initializing frequency table\n");
 
-	// initialize frequency table
+	/* initialize frequency table */
 	for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
 		cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index;
 		pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
@@ -235,10 +235,10 @@ static int cbe_cpufreq_cpu_init (struct 
 	policy->cpus = cpu_sibling_map[policy->cpu];
 #endif
 
-	cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu);
+	cpufreq_frequency_table_get_attr(cbe_freqs, policy->cpu);
 
 	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */
-	return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs);
+	return cpufreq_frequency_table_cpuinfo(policy, cbe_freqs);
 }
 
 static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy)
@@ -270,7 +270,7 @@ static int cbe_cpufreq_target(struct cpu
 	freqs.new = cbe_freqs[cbe_pmode_new].frequency;
 	freqs.cpu = policy->cpu;
 
-	mutex_lock (&cbe_switch_mutex);
+	mutex_lock(&cbe_switch_mutex);
 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
 	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
@@ -303,6 +303,9 @@ static int __init cbe_cpufreq_init(void)
 {
 	struct device_node *np;
 
+	if (!machine_is(cell))
+		return 0;
+
 	np = of_find_node_by_type(NULL, "ibm,pmi");
 
 	pmi_dev = of_find_device_by_node(np);
@@ -315,7 +318,7 @@ static int __init cbe_cpufreq_init(void)
 
 static void __exit cbe_cpufreq_exit(void)
 {
-	if(pmi_dev)
+	if (pmi_dev)
 		pmi_unregister_handler(pmi_dev, &cbe_pmi_handler);
 
 	cpufreq_unregister_driver(&cbe_cpufreq_driver);

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 20:38           ` Olof Johansson
@ 2007-04-27  0:10             ` Segher Boessenkool
  0 siblings, 0 replies; 29+ messages in thread
From: Segher Boessenkool @ 2007-04-27  0:10 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, paulus, egor, Arnd Bergmann, cpufreq

>> You probably should map the SDC registers in the
>> platform startop code, and then have the various
>> platform drivers use that.  Maybe even do an
>> abstraction ;-)
>
> Yep, it's a good idea to do and I'll look at it for 2.6.23 time frame.

Oh I'm sure you can get that done in time for .22 still ;-)


Segher

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Cbe-oss-dev] [PATCH] cell: cbe_cpufreq cleanup and crash fix
  2007-04-26 23:07     ` [PATCH] cell: cbe_cpufreq cleanup and crash fix Olof Johansson
@ 2007-04-27  5:22       ` Akinobu Mita
  2007-04-27  5:32         ` Olof Johansson
  2007-04-27  5:33       ` [PATCH v2] " Olof Johansson
  1 sibling, 1 reply; 29+ messages in thread
From: Akinobu Mita @ 2007-04-27  5:22 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, cbe-oss-dev, Arnd Bergmann

On Thu, Apr 26, 2007 at 06:07:54PM -0500, Olof Johansson wrote:
> @@ -303,6 +303,9 @@ static int __init cbe_cpufreq_init(void)
>  {
>  	struct device_node *np;
>  
> +	if (!machine_is(cell))
> +		return 0;
> +

Please return -ENODEV to prevent loading module.

Otherwise it will cause crash when unloading the module
because of cpufreq_unregister_driver() with unregistered driver.

> @@ -315,7 +318,7 @@ static int __init cbe_cpufreq_init(void)
>  
>  static void __exit cbe_cpufreq_exit(void)
>  {
> -	if(pmi_dev)
> +	if (pmi_dev)
>  		pmi_unregister_handler(pmi_dev, &cbe_pmi_handler);
>  
>  	cpufreq_unregister_driver(&cbe_cpufreq_driver);

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [Cbe-oss-dev] [PATCH] cell: cbe_cpufreq cleanup and crash fix
  2007-04-27  5:22       ` [Cbe-oss-dev] " Akinobu Mita
@ 2007-04-27  5:32         ` Olof Johansson
  0 siblings, 0 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-27  5:32 UTC (permalink / raw)
  To: Akinobu Mita; +Cc: linuxppc-dev, cbe-oss-dev, Arnd Bergmann

On Fri, Apr 27, 2007 at 02:22:06PM +0900, Akinobu Mita wrote:
> On Thu, Apr 26, 2007 at 06:07:54PM -0500, Olof Johansson wrote:
> > @@ -303,6 +303,9 @@ static int __init cbe_cpufreq_init(void)
> >  {
> >  	struct device_node *np;
> >  
> > +	if (!machine_is(cell))
> > +		return 0;
> > +
> 
> Please return -ENODEV to prevent loading module.
> 
> Otherwise it will cause crash when unloading the module
> because of cpufreq_unregister_driver() with unregistered driver.

It won't crash due to the checks in cpufreq_unregister_driver(), but
it's still better to return failure.


Thanks,

-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v2] cell: cbe_cpufreq cleanup and crash fix
  2007-04-26 23:07     ` [PATCH] cell: cbe_cpufreq cleanup and crash fix Olof Johansson
  2007-04-27  5:22       ` [Cbe-oss-dev] " Akinobu Mita
@ 2007-04-27  5:33       ` Olof Johansson
  2007-04-27  7:55         ` Christian Krafft
  1 sibling, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-27  5:33 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, cbe-oss-dev, Christian Krafft

cbe_cpufreq cleanups:

* comment format
* whitespace
* don't init on non-cell platforms

Signed-off-by: Olof Johansson <olof@lixom.net>

Index: powerpc/arch/powerpc/platforms/cell/cbe_cpufreq.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/cell/cbe_cpufreq.c
+++ powerpc/arch/powerpc/platforms/cell/cbe_cpufreq.c
@@ -155,7 +155,7 @@ static int set_pmode_reg(int cpu, unsign
 }
 
 static int set_pmode(int cpu, unsigned int slow_mode) {
-	if(pmi_dev)
+	if (pmi_dev)
 		return set_pmode_pmi(cpu, slow_mode);
 	else
 		return set_pmode_reg(cpu, slow_mode);
@@ -167,7 +167,7 @@ static void cbe_cpufreq_handle_pmi(struc
 	u8 cpu;
 	u8 cbe_pmode_new;
 
-	BUG_ON (pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
+	BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE);
 
 	cpu = cbe_node_to_cpu(pmi_msg.data1);
 	cbe_pmode_new = pmi_msg.data2;
@@ -191,7 +191,7 @@ static struct pmi_handler cbe_pmi_handle
  * cpufreq functions
  */
 
-static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy)
+static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	const u32 *max_freqp;
 	u32 max_freq;
@@ -200,7 +200,7 @@ static int cbe_cpufreq_cpu_init (struct 
 
 	cpu = of_get_cpu_node(policy->cpu, NULL);
 
-	if(!cpu)
+	if (!cpu)
 		return -ENODEV;
 
 	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
@@ -210,13 +210,13 @@ static int cbe_cpufreq_cpu_init (struct 
 	if (!max_freqp)
 		return -EINVAL;
 
-	// we need the freq in kHz
+	/* we need the freq in kHz */
 	max_freq = *max_freqp / 1000;
 
 	pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 	pr_debug("initializing frequency table\n");
 
-	// initialize frequency table
+	/* initialize frequency table */
 	for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
 		cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index;
 		pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
@@ -235,10 +235,10 @@ static int cbe_cpufreq_cpu_init (struct 
 	policy->cpus = cpu_sibling_map[policy->cpu];
 #endif
 
-	cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu);
+	cpufreq_frequency_table_get_attr(cbe_freqs, policy->cpu);
 
 	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */
-	return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs);
+	return cpufreq_frequency_table_cpuinfo(policy, cbe_freqs);
 }
 
 static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy)
@@ -270,7 +270,7 @@ static int cbe_cpufreq_target(struct cpu
 	freqs.new = cbe_freqs[cbe_pmode_new].frequency;
 	freqs.cpu = policy->cpu;
 
-	mutex_lock (&cbe_switch_mutex);
+	mutex_lock(&cbe_switch_mutex);
 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
 	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
@@ -303,6 +303,9 @@ static int __init cbe_cpufreq_init(void)
 {
 	struct device_node *np;
 
+	if (!machine_is(cell))
+		return -ENODEV;
+
 	np = of_find_node_by_type(NULL, "ibm,pmi");
 
 	pmi_dev = of_find_device_by_node(np);
@@ -315,7 +318,7 @@ static int __init cbe_cpufreq_init(void)
 
 static void __exit cbe_cpufreq_exit(void)
 {
-	if(pmi_dev)
+	if (pmi_dev)
 		pmi_unregister_handler(pmi_dev, &cbe_pmi_handler);
 
 	cpufreq_unregister_driver(&cbe_cpufreq_driver);

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v3] [2.6.22] pasemi: cpufreq driver
  2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
  2007-04-26  8:55   ` Arnd Bergmann
  2007-04-26 10:26   ` Johannes Berg
@ 2007-04-27  5:46   ` Olof Johansson
  2007-04-29  2:50     ` Dominik Brodowski
  2 siblings, 1 reply; 29+ messages in thread
From: Olof Johansson @ 2007-04-27  5:46 UTC (permalink / raw)
  To: paulus; +Cc: linuxppc-dev, egor, arnd, cpufreq

Cpufreq driver for PA Semi PWRficient processors.

Signed-off-by: Egor Martovetsky <egor@pasemi.com>
Signed-off-by: Olof Johansson <olof@lixom.net>

---

v3:
* use cpu_online_map instead of possible_map
* fix CPU_FREQ dependency
* return -ENODEV instead of 0 on unsupported platforms

v2:
* Attributed copyright correctly to cbe_cpufreq.c and adjust license
  to match (this was my mistake)
* Lookup the SDC and Gizmo device nodes to get register bases
* machine_is_compatible checks
* Cleanups as suggested by Arnd + misc whitespace

I chose not to do this as an of_platform driver since it doesn't fit
that well with the cpufreq driver model; having 3 levels of init/probe
functions is excessive.

Index: powerpc/arch/powerpc/platforms/pasemi/Makefile
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/Makefile
+++ powerpc/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,3 @@
 obj-y	+= setup.o pci.o time.o idle.o powersave.o iommu.o
 obj-$(CONFIG_PPC_PASEMI_MDIO)	+= gpio_mdio.o
+obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o
Index: powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
===================================================================
--- /dev/null
+++ powerpc/arch/powerpc/platforms/pasemi/cpufreq.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Authors: Egor Martovetsky <egor@pasemi.com>
+ *	    Olof Johansson <olof@lixom.net>
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/timer.h>
+
+#include <asm/hw_irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#define SDCASR_REG		0x0100
+#define SDCASR_REG_STRIDE	0x1000
+#define SDCPWR_CFGA0_REG	0x0100
+#define SDCPWR_PWST0_REG	0x0000
+#define SDCPWR_GIZTIME_REG	0x0440
+
+/* SDCPWR_GIZTIME_REG fields */
+#define SDCPWR_GIZTIME_GR	0x80000000
+#define SDCPWR_GIZTIME_LONGLOCK	0x000000ff
+
+/* Offset of ASR registers from SDC base */
+#define SDCASR_OFFSET		0x120000
+
+static void __iomem *sdcpwr_mapbase;
+static void __iomem *sdcasr_mapbase;
+
+static DEFINE_MUTEX(pas_switch_mutex);
+
+/* Current astate, is used when waking up from power savings on
+ * one core, in case the other core has switched states during
+ * the idle time.
+ */
+static int current_astate;
+
+/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
+static struct cpufreq_frequency_table pas_freqs[] = {
+	{0,	0},
+	{1,	0},
+	{2,	0},
+	{3,	0},
+	{4,	0},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+static struct freq_attr *pas_cpu_freqs_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+/*
+ * hardware specific functions
+ */
+
+static int get_astate_freq(int astate)
+{
+	u32 ret;
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
+
+	return ret & 0x3f;
+}
+
+static int get_cur_astate(int cpu)
+{
+	u32 ret;
+
+	ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
+	ret = (ret >> (cpu * 4)) & 0x7;
+
+	return ret;
+}
+
+static int get_gizmo_latency(void)
+{
+	u32 giztime, ret;
+
+	giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
+
+	/* just provide the upper bound */
+	if (giztime & SDCPWR_GIZTIME_GR)
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
+	else
+		ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
+
+	return ret;
+}
+
+static void set_astate(int cpu, unsigned int astate)
+{
+	u64 flags;
+
+	/* Return if called before init has run */
+	if (unlikely(!sdcasr_mapbase))
+		return;
+
+	local_irq_save(flags);
+
+	out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
+
+	local_irq_restore(flags);
+}
+
+void restore_astate(int cpu)
+{
+	set_astate(cpu, current_astate);
+}
+
+/*
+ * cpufreq functions
+ */
+
+static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	u32 *max_freq;
+	int i, cur_astate;
+	struct resource res;
+	struct device_node *cpu, *dn;
+	int err = -ENODEV;
+
+	cpu = of_get_cpu_node(policy->cpu, NULL);
+
+	if (!cpu)
+		goto out;
+
+	dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
+	if (!dn)
+		goto out;
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out;
+	sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
+	if (!sdcasr_mapbase) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
+	if (!dn) {
+		err = -ENODEV;
+		goto out_unmap_sdcasr;
+	}
+	err = of_address_to_resource(dn, 0, &res);
+	of_node_put(dn);
+	if (err)
+		goto out_unmap_sdcasr;
+	sdcpwr_mapbase = ioremap(res.start, 0x1000);
+	if (!sdcpwr_mapbase) {
+		err = -EINVAL;
+		goto out_unmap_sdcasr;
+	}
+
+	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
+
+	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
+	if (!max_freq) {
+		err = -EINVAL;
+		goto out_unmap_sdcpwr;
+	}
+
+	/* we need the freq in kHz */
+	*max_freq /= 1000;
+
+	pr_debug("max clock-frequency is at %u kHz\n", *max_freq);
+	pr_debug("initializing frequency table\n");
+
+	/* initialize frequency table */
+	for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
+		pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
+		pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+	}
+
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+	policy->cpuinfo.transition_latency = get_gizmo_latency();
+
+	cur_astate = get_cur_astate(policy->cpu);
+	pr_debug("current astate is at %d\n",cur_astate);
+
+	policy->cur = pas_freqs[cur_astate].frequency;
+	policy->cpus = cpu_online_map;
+
+	cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
+
+	/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
+	 * are set correctly
+	 */
+	return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
+
+out_unmap_sdcpwr:
+	iounmap(sdcpwr_mapbase);
+
+out_unmap_sdcasr:
+	iounmap(sdcasr_mapbase);
+out:
+	return err;
+}
+
+static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+	if (sdcasr_mapbase)
+		iounmap(sdcasr_mapbase);
+	if (sdcpwr_mapbase)
+		iounmap(sdcpwr_mapbase);
+
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static int pas_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, pas_freqs);
+}
+
+static int pas_cpufreq_target(struct cpufreq_policy *policy,
+			      unsigned int target_freq,
+			      unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	int pas_astate_new;
+	int i;
+
+	cpufreq_frequency_table_target(policy,
+				       pas_freqs,
+				       target_freq,
+				       relation,
+				       &pas_astate_new);
+
+	freqs.old = policy->cur;
+	freqs.new = pas_freqs[pas_astate_new].frequency;
+	freqs.cpu = policy->cpu;
+
+	mutex_lock(&pas_switch_mutex);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
+		 policy->cpu,
+		 pas_freqs[pas_astate_new].frequency,
+		 pas_freqs[pas_astate_new].index);
+
+	current_astate = pas_astate_new;
+
+	for_each_online_cpu(i)
+		set_astate(i, pas_astate_new);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+	mutex_unlock(&pas_switch_mutex);
+
+	return 0;
+}
+
+static struct cpufreq_driver pas_cpufreq_driver = {
+	.name		= "pas-cpufreq",
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= pas_cpufreq_cpu_init,
+	.exit		= pas_cpufreq_cpu_exit,
+	.verify		= pas_cpufreq_verify,
+	.target		= pas_cpufreq_target,
+	.attr		= pas_cpu_freqs_attr,
+};
+
+/*
+ * module init and destoy
+ */
+
+static int __init pas_cpufreq_init(void)
+{
+	if (!machine_is_compatible("PA6T-1682M"))
+		return -ENODEV;
+
+	return cpufreq_register_driver(&pas_cpufreq_driver);
+}
+
+static void __exit pas_cpufreq_exit(void)
+{
+	cpufreq_unregister_driver(&pas_cpufreq_driver);
+}
+
+module_init(pas_cpufreq_init);
+module_exit(pas_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
Index: powerpc/arch/powerpc/platforms/pasemi/idle.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/idle.c
+++ powerpc/arch/powerpc/platforms/pasemi/idle.c
@@ -61,6 +61,10 @@ static int pasemi_system_reset_exception
 		/* do system reset */
 		return 0;
 	}
+
+	/* Set higher astate since we come out of power savings at 0 */
+	restore_astate(hard_smp_processor_id());
+
 	/* everything handled */
 	regs->msr |= MSR_RI;
 	return 1;
@@ -68,6 +72,11 @@ static int pasemi_system_reset_exception
 
 void __init pasemi_idle_init(void)
 {
+#ifndef CONFIG_PPC_PASEMI_CPUFREQ
+	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
+	current_mode = 0;
+#endif
+
 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
 	ppc_md.power_save = modes[current_mode].entry;
 	printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
Index: powerpc/arch/powerpc/platforms/pasemi/pasemi.h
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pasemi/pasemi.h
+++ powerpc/arch/powerpc/platforms/pasemi/pasemi.h
@@ -14,6 +14,14 @@ extern void __init pasemi_idle_init(void
 extern void idle_spin(void);
 extern void idle_doze(void);
 
+/* Restore astate to last set */
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+extern void restore_astate(int cpu);
+#else
+static inline void restore_astate(int cpu)
+{
+}
+#endif
 
 
 #endif /* _PASEMI_PASEMI_H */
Index: powerpc/arch/powerpc/platforms/Kconfig
===================================================================
--- powerpc.orig/arch/powerpc/platforms/Kconfig
+++ powerpc/arch/powerpc/platforms/Kconfig
@@ -169,6 +169,16 @@ config CPU_FREQ_PMAC64
 	help
 	  This adds support for frequency switching on Apple iMac G5,
 	  and some of the more recent desktop G5 machines as well.
+
+config PPC_PASEMI_CPUFREQ
+	bool "Support for PA Semi PWRficient"
+	depends on CPU_FREQ && PPC_PASEMI
+	default y
+	select CPU_FREQ_TABLE
+	help
+	  This adds the support for frequency switching on PA Semi
+	  PWRficient processors.
+
 endmenu
 
 config PPC601_SYNC_FIX

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] cell: cbe_cpufreq cleanup and crash fix
  2007-04-27  5:33       ` [PATCH v2] " Olof Johansson
@ 2007-04-27  7:55         ` Christian Krafft
  0 siblings, 0 replies; 29+ messages in thread
From: Christian Krafft @ 2007-04-27  7:55 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, cbe-oss-dev, Arnd Bergmann

[-- Attachment #1: Type: text/plain, Size: 633 bytes --]

On Fri, 27 Apr 2007 00:33:47 -0500
olof@lixom.net (Olof Johansson) wrote:

> cbe_cpufreq cleanups:
> 
> * comment format
> * whitespace
> * don't init on non-cell platforms
> 
> Signed-off-by: Olof Johansson <olof@lixom.net>

Acked-by: Christian Krafft <krafft@de.ibm.com>

Thx for fixing ;-)

-- 
Mit freundlichen Gruessen,
kind regards,

Christian Krafft
IBM Systems & Technology Group,
Linux Kernel Development
IT Specialist


Vorsitzender des Aufsichtsrats:	Johann Weihen
Geschaeftsfuehrung:		Herbert Kircher
Sitz der Gesellschaft:		Boeblingen
Registriergericht:		Amtsgericht Stuttgart, HRB 243294


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-26 20:37     ` Olof Johansson
@ 2007-04-27  9:40       ` Johannes Berg
  2007-04-27 18:09         ` Olof Johansson
  0 siblings, 1 reply; 29+ messages in thread
From: Johannes Berg @ 2007-04-27  9:40 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, egor, paulus, arnd, cpufreq

[-- Attachment #1: Type: text/plain, Size: 899 bytes --]

On Thu, 2007-04-26 at 15:37 -0500, Olof Johansson wrote:

> You can set the requested speed per-processor, but the actual speed
> will be the max of them. Because of this, it's less surprising to set
> it on both at the same time since then you'll at least know what you're
> running at.
> 
> So yes, I'll change it to online_map instead of possible_map.

Ok, yeah, so effectively they all just go together (that additional
complication of running at the max probably should be hidden as you
note)

> Hmm, I just tried adding debugging to the cpu_init code, and I'm not
> seeing it called more than once (i.e. only for cpu 0). Mind you, I don't
> have cpu hotplug support at this time.

Oh right, you need cpu hotplug support for the bug to show up. What
happens then is you unplug and replug a CPU and the sysfs cpufreq stuff
for the CPU you just replugged is gone.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v2] [2.6.22] pasemi: cpufreq driver
  2007-04-27  9:40       ` Johannes Berg
@ 2007-04-27 18:09         ` Olof Johansson
  0 siblings, 0 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-27 18:09 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev, egor, paulus, arnd, cpufreq

On Fri, Apr 27, 2007 at 11:40:03AM +0200, Johannes Berg wrote:

> > Hmm, I just tried adding debugging to the cpu_init code, and I'm not
> > seeing it called more than once (i.e. only for cpu 0). Mind you, I don't
> > have cpu hotplug support at this time.
> 
> Oh right, you need cpu hotplug support for the bug to show up. What
> happens then is you unplug and replug a CPU and the sysfs cpufreq stuff
> for the CPU you just replugged is gone.

Ok. Nothing for me to worry about right now then, but it's good to keep
in mind for when the time comes.


Thanks,

-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v3] [2.6.22] pasemi: cpufreq driver
  2007-04-27  5:46   ` [PATCH v3] " Olof Johansson
@ 2007-04-29  2:50     ` Dominik Brodowski
  2007-04-29  3:40       ` Stephen Rothwell
  2007-04-29  4:42       ` Olof Johansson
  0 siblings, 2 replies; 29+ messages in thread
From: Dominik Brodowski @ 2007-04-29  2:50 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, egor, paulus, arnd, cpufreq

Hi,

On Fri, Apr 27, 2007 at 12:46:01AM -0500, Olof Johansson wrote:
> +	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);

(u32) or (32*) ?

> +	cur_astate = get_cur_astate(policy->cpu);

May the different cores have different settings at initalization? e.g.

core 0:		freq A
core 1:		freq B

with (freq B > freq A)? If so, cur_astate is set wrongly, as the _effective_
frequency would be freq B, right?

> +#ifndef CONFIG_PPC_PASEMI_CPUFREQ
> +	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
> +	current_mode = 0;
> +#endif
> +

This confuses me a bit -- does something else than cpufreq not work if
cpufreq is disabled?


Overall, the patch looks good to me.

	Dominik

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v3] [2.6.22] pasemi: cpufreq driver
  2007-04-29  2:50     ` Dominik Brodowski
@ 2007-04-29  3:40       ` Stephen Rothwell
  2007-04-29  4:39         ` Olof Johansson
  2007-04-29  4:42       ` Olof Johansson
  1 sibling, 1 reply; 29+ messages in thread
From: Stephen Rothwell @ 2007-04-29  3:40 UTC (permalink / raw)
  To: Dominik Brodowski
  Cc: arnd, cpufreq, linuxppc-dev, egor, Olof Johansson, paulus

On Sat, 28 Apr 2007 22:50:21 -0400 Dominik Brodowski <linux@dominikbrodowski.net> wrote:
>
> Hi,
> 
> On Fri, Apr 27, 2007 at 12:46:01AM -0500, Olof Johansson wrote:
> > +	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
> 
> (u32) or (32*) ?

Neither, get_property now returns (void *) so casting is not needed or
wanted.

Also, later in the patch, you modify the property value in place.  Do not do this. To help you remember, if you had not done the cast, gcc would have complained because get_property return a const void *.

Also, get_property is now called of_get_property.
-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v3] [2.6.22] pasemi: cpufreq driver
  2007-04-29  3:40       ` Stephen Rothwell
@ 2007-04-29  4:39         ` Olof Johansson
  0 siblings, 0 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-29  4:39 UTC (permalink / raw)
  To: Stephen Rothwell
  Cc: arnd, cpufreq, Dominik Brodowski, linuxppc-dev, paulus, egor

On Sun, Apr 29, 2007 at 01:40:59PM +1000, Stephen Rothwell wrote:
> On Sat, 28 Apr 2007 22:50:21 -0400 Dominik Brodowski <linux@dominikbrodowski.net> wrote:
> >
> > Hi,
> > 
> > On Fri, Apr 27, 2007 at 12:46:01AM -0500, Olof Johansson wrote:
> > > +	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
> > 
> > (u32) or (32*) ?
> 
> Neither, get_property now returns (void *) so casting is not needed or
> wanted.
> 
> Also, later in the patch, you modify the property value in place.  Do not do this. To help you remember, if you had not done the cast, gcc would have complained because get_property return a const void *.

Yep. It came along over from the cbe_cpufreq driver, which also does
it. I'll post a patch tomorrow or Monday unless someone else beats me
to it (the driver has already been merged).

> Also, get_property is now called of_get_property.

Sure, but keeping up with the API churn during other patch merging is a mess. It's easier to
make one final pass over all new code and fix it up once more later during the merge window.


-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v3] [2.6.22] pasemi: cpufreq driver
  2007-04-29  2:50     ` Dominik Brodowski
  2007-04-29  3:40       ` Stephen Rothwell
@ 2007-04-29  4:42       ` Olof Johansson
  1 sibling, 0 replies; 29+ messages in thread
From: Olof Johansson @ 2007-04-29  4:42 UTC (permalink / raw)
  To: paulus, linuxppc-dev, egor, arnd, cpufreq

On Sat, Apr 28, 2007 at 10:50:21PM -0400, Dominik Brodowski wrote:
> Hi,
> 
> On Fri, Apr 27, 2007 at 12:46:01AM -0500, Olof Johansson wrote:
> > +	max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
> 
> (u32) or (32*) ?
> 
> > +	cur_astate = get_cur_astate(policy->cpu);
> 
> May the different cores have different settings at initalization? e.g.
> 
> core 0:		freq A
> core 1:		freq B
> 
> with (freq B > freq A)? If so, cur_astate is set wrongly, as the _effective_
> frequency would be freq B, right?

Firmware normally sets only cpu 0 (since cpu 1 isn't started until after
linux is loaded and started). So it'll works well by default.

> > +#ifndef CONFIG_PPC_PASEMI_CPUFREQ
> > +	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
> > +	current_mode = 0;
> > +#endif
> > +
> 
> This confuses me a bit -- does something else than cpufreq not work if
> cpufreq is disabled?

After doze, we come back out in astate 0, so we need to raise it back
up. We do so by calling the restore_astate() function. If we don't have
it, and use idle=doze, we'll end up with a cpu running at astate 0 and
no way to raise it.

> Overall, the patch looks good to me.


Thanks,

-Olof

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2007-04-29  4:41 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-25 20:46 [PATCH] [2.6.22] pasemi: cpufreq driver Olof Johansson
2007-04-25 23:47 ` Arnd Bergmann
2007-04-25 23:57   ` Olof Johansson
2007-04-26  1:57     ` Olof Johansson
2007-04-26  6:56   ` cbe_cpufreq crashes my machine Olof Johansson
2007-04-26  8:39     ` Benjamin Herrenschmidt
2007-04-26 23:07     ` [PATCH] cell: cbe_cpufreq cleanup and crash fix Olof Johansson
2007-04-27  5:22       ` [Cbe-oss-dev] " Akinobu Mita
2007-04-27  5:32         ` Olof Johansson
2007-04-27  5:33       ` [PATCH v2] " Olof Johansson
2007-04-27  7:55         ` Christian Krafft
2007-04-26  5:37 ` [PATCH v2] [2.6.22] pasemi: cpufreq driver Olof Johansson
2007-04-26  8:55   ` Arnd Bergmann
2007-04-26 16:48     ` Olof Johansson
2007-04-26 17:11       ` Arnd Bergmann
2007-04-26 19:05         ` Segher Boessenkool
2007-04-26 20:38           ` Olof Johansson
2007-04-27  0:10             ` Segher Boessenkool
2007-04-26 20:26         ` Olof Johansson
2007-04-26 20:43           ` Arnd Bergmann
2007-04-26 10:26   ` Johannes Berg
2007-04-26 20:37     ` Olof Johansson
2007-04-27  9:40       ` Johannes Berg
2007-04-27 18:09         ` Olof Johansson
2007-04-27  5:46   ` [PATCH v3] " Olof Johansson
2007-04-29  2:50     ` Dominik Brodowski
2007-04-29  3:40       ` Stephen Rothwell
2007-04-29  4:39         ` Olof Johansson
2007-04-29  4:42       ` Olof Johansson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).