Linux MIPS Architecture development
 help / color / mirror / Atom feed
* [PATCH v4 0/10] Alchemy updates.
@ 2008-07-29 16:58 Manuel Lauss
  2008-07-29 16:59 ` [PATCH v4 1/10] Alchemy: remove get/set_au1x00_lcd_clock() Manuel Lauss
                   ` (11 more replies)
  0 siblings, 12 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 16:58 UTC (permalink / raw)
  To: linux-mips

Hello,

Here's again a new set of patches to modernize Alchemy setup and PM code.
All patches have been compile-tested with db1100 and db1200 defconfigs,
and have been runnning on a few custom Au1250 boards for now more than
5 weeks.  I've suspended and resumed a few hundred times while stressing
the system (continuous SD + CF reads while playing some wave files and
compiling sources).

#1 removes unused functions
#2 removes the cpu_table and replaces it with simpler code (IMHO of course)
#3 enables use of cp0 counter as a fallback,
#4 clockevent/clocksource support using one of the 2 counters of the Au1xxx
   this also enables the use of the 'wait' instruction; depends on #3
#5 cleanup made possible with #4 
#7 and #8 fix suspend/resume.
#9 adds DBDMA suspend/resume support.
#10 replaces sysctl suspend interface with something better (IMO).

All patches depend on each other, have been run-tested on a custom AU1200
system and compile-tested with a minimal config on db1100 and db1200.

Changes V3->V4:
- rediffed against 2.6.27-rc1
- add patch #10.

Changes V2->V3:
- swap patches 1 and 2 
- minor refinements, no function changes.

Changes V1->V2:
- address Sergei's comments wrt. config[OD] handling
- change TOY clocksource to RTC clocksource
- add another patch (#5)


 arch/mips/Kconfig                     |    8 
 arch/mips/au1000/Kconfig              |    4 
 arch/mips/au1000/common/Makefile      |    4 
 arch/mips/au1000/common/clocks.c      |   65 +++--
 arch/mips/au1000/common/cputable.c    |   52 ----
 arch/mips/au1000/common/dbdma.c       |   65 +++++
 arch/mips/au1000/common/dbg_io.c      |    4 
 arch/mips/au1000/common/irq.c         |   57 ----
 arch/mips/au1000/common/platform.c    |  257 ++++++++++++++++++++
 arch/mips/au1000/common/power.c       |  421 ++++++----------------------------
 arch/mips/au1000/common/setup.c       |   39 ---
 arch/mips/au1000/common/sleeper.S     |  121 +++++----
 arch/mips/au1000/common/time.c        |  305 ++++++++----------------
 arch/mips/au1000/db1x00/Makefile      |    1 
 arch/mips/au1000/mtx-1/Makefile       |    2 
 arch/mips/au1000/pb1000/Makefile      |    1 
 arch/mips/au1000/pb1100/Makefile      |    1 
 arch/mips/au1000/pb1200/Makefile      |    2 
 arch/mips/au1000/pb1500/Makefile      |    1 
 arch/mips/au1000/pb1550/Makefile      |    1 
 arch/mips/au1000/xxs1500/Makefile     |    1 
 arch/mips/kernel/Makefile             |    4 
 arch/mips/kernel/cevt-r4k.c           |    2 
 arch/mips/kernel/csrc-r4k.c           |    2 
 include/asm-mips/mach-au1x00/au1000.h |   64 +++--
 include/asm-mips/time.h               |   24 +
 26 files changed, 719 insertions(+), 789 deletions(-)

Thanks,
	Manuel Lauss

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

* [PATCH v4 1/10] Alchemy: remove get/set_au1x00_lcd_clock().
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
@ 2008-07-29 16:59 ` Manuel Lauss
  2008-07-29 17:00 ` [PATCH v4 2/10] Alchemy: remove cpu_table Manuel Lauss
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 16:59 UTC (permalink / raw)
  To: linux-mips

There are no in-tree users, so remove them.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/clocks.c      |   31 -------------------------------
 arch/mips/au1000/common/time.c        |    1 -
 include/asm-mips/mach-au1x00/au1000.h |    2 --
 3 files changed, 0 insertions(+), 34 deletions(-)

diff --git a/arch/mips/au1000/common/clocks.c b/arch/mips/au1000/common/clocks.c
index 043429d..a8170fd 100644
--- a/arch/mips/au1000/common/clocks.c
+++ b/arch/mips/au1000/common/clocks.c
@@ -30,7 +30,6 @@
 #include <asm/mach-au1x00/au1000.h>
 
 static unsigned int au1x00_clock; /*  Hz */
-static unsigned int lcd_clock;    /* KHz */
 static unsigned long uart_baud_base;
 
 /*
@@ -61,33 +60,3 @@ void set_au1x00_uart_baud_base(unsigned long new_baud_base)
 {
 	uart_baud_base = new_baud_base;
 }
-
-/*
- * Calculate the Au1x00's LCD clock based on the current
- * cpu clock and the system bus clock, and try to keep it
- * below 40 MHz (the Pb1000 board can lock-up if the LCD
- * clock is over 40 MHz).
- */
-void set_au1x00_lcd_clock(void)
-{
-	unsigned int static_cfg0;
-	unsigned int sys_busclk = (get_au1x00_speed() / 1000) /
-				  ((int)(au_readl(SYS_POWERCTRL) & 0x03) + 2);
-
-	static_cfg0 = au_readl(MEM_STCFG0);
-
-	if (static_cfg0 & (1 << 11))
-		lcd_clock = sys_busclk / 5; /* note: BCLK switching fails with D5 */
-	else
-		lcd_clock = sys_busclk / 4;
-
-	if (lcd_clock > 50000) /* Epson MAX */
-		printk(KERN_WARNING "warning: LCD clock too high (%u KHz)\n",
-				    lcd_clock);
-}
-
-unsigned int get_au1x00_lcd_clock(void)
-{
-	return lcd_clock;
-}
-EXPORT_SYMBOL(get_au1x00_lcd_clock);
diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c
index 563d939..68d7142 100644
--- a/arch/mips/au1000/common/time.c
+++ b/arch/mips/au1000/common/time.c
@@ -224,7 +224,6 @@ void __init plat_time_init(void)
 	printk(KERN_INFO "CPU frequency %u.%02u MHz\n",
 	       est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
 	set_au1x00_speed(est_freq);
-	set_au1x00_lcd_clock(); /* program the LCD clock */
 
 #ifdef CONFIG_PM
 	/*
diff --git a/include/asm-mips/mach-au1x00/au1000.h b/include/asm-mips/mach-au1x00/au1000.h
index 0d302ba..8d2ced6 100644
--- a/include/asm-mips/mach-au1x00/au1000.h
+++ b/include/asm-mips/mach-au1x00/au1000.h
@@ -97,8 +97,6 @@ extern void set_au1x00_speed(unsigned int new_freq);
 extern unsigned int get_au1x00_speed(void);
 extern void set_au1x00_uart_baud_base(unsigned long new_baud_base);
 extern unsigned long get_au1x00_uart_baud_base(void);
-extern void set_au1x00_lcd_clock(void);
-extern unsigned int get_au1x00_lcd_clock(void);
 
 /*
  * Every board describes its IRQ mapping with this table.
-- 
1.5.6.3

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

* [PATCH v4 2/10] Alchemy: remove cpu_table.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
  2008-07-29 16:59 ` [PATCH v4 1/10] Alchemy: remove get/set_au1x00_lcd_clock() Manuel Lauss
@ 2008-07-29 17:00 ` Manuel Lauss
  2008-07-29 17:00 ` [PATCH v4 3/10] MIPS: make cp0 counter clocksource/event usable as fallback Manuel Lauss
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:00 UTC (permalink / raw)
  To: linux-mips

Remove the cpu_table:
- move detection of whether c0_config[OD] is read-only and should be set
  to fix various chip errata to au1000 headers.
- move detection of write-only sys_cpupll to au1000 headers.
- remove the BCLK switching code:  Activation of this features should be
  left to the boards using the chips since it also affects external devices
  tied to BCLK, and only the board designers know whether it is safe to
  enable.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/Makefile      |    2 +-
 arch/mips/au1000/common/cputable.c    |   52 -----------------------------
 arch/mips/au1000/common/setup.c       |   26 +--------------
 arch/mips/au1000/common/time.c        |    4 +-
 include/asm-mips/mach-au1x00/au1000.h |   58 +++++++++++++++++++++-----------
 5 files changed, 42 insertions(+), 100 deletions(-)
 delete mode 100644 arch/mips/au1000/common/cputable.c

diff --git a/arch/mips/au1000/common/Makefile b/arch/mips/au1000/common/Makefile
index dd0e19d..850de08 100644
--- a/arch/mips/au1000/common/Makefile
+++ b/arch/mips/au1000/common/Makefile
@@ -7,7 +7,7 @@
 
 obj-y += prom.o irq.o puts.o time.o reset.o \
 	au1xxx_irqmap.o clocks.o platform.o power.o setup.o \
-	sleeper.o cputable.o dma.o dbdma.o gpio.o
+	sleeper.o dma.o dbdma.o gpio.o
 
 obj-$(CONFIG_KGDB)		+= dbg_io.o
 obj-$(CONFIG_PCI)		+= pci.o
diff --git a/arch/mips/au1000/common/cputable.c b/arch/mips/au1000/common/cputable.c
deleted file mode 100644
index ba6430b..0000000
--- a/arch/mips/au1000/common/cputable.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  arch/mips/au1000/common/cputable.c
- *
- *  Copyright (C) 2004 Dan Malek (dan@embeddededge.com)
- *	Copied from PowerPC and updated for Alchemy Au1xxx processors.
- *
- *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
- *
- *  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 of the License, or (at your option) any later version.
- */
-
-#include <asm/mach-au1x00/au1000.h>
-
-struct cpu_spec *cur_cpu_spec[NR_CPUS];
-
-/* With some thought, we can probably use the mask to reduce the
- * size of the table.
- */
-struct cpu_spec cpu_specs[] = {
-	{ 0xffffffff, 0x00030100, "Au1000 DA", 1, 0, 1 },
-	{ 0xffffffff, 0x00030201, "Au1000 HA", 1, 0, 1 },
-	{ 0xffffffff, 0x00030202, "Au1000 HB", 1, 0, 1 },
-	{ 0xffffffff, 0x00030203, "Au1000 HC", 1, 1, 0 },
-	{ 0xffffffff, 0x00030204, "Au1000 HD", 1, 1, 0 },
-	{ 0xffffffff, 0x01030200, "Au1500 AB", 1, 1, 0 },
-	{ 0xffffffff, 0x01030201, "Au1500 AC", 0, 1, 0 },
-	{ 0xffffffff, 0x01030202, "Au1500 AD", 0, 1, 0 },
-	{ 0xffffffff, 0x02030200, "Au1100 AB", 1, 1, 0 },
-	{ 0xffffffff, 0x02030201, "Au1100 BA", 1, 1, 0 },
-	{ 0xffffffff, 0x02030202, "Au1100 BC", 1, 1, 0 },
-	{ 0xffffffff, 0x02030203, "Au1100 BD", 0, 1, 0 },
-	{ 0xffffffff, 0x02030204, "Au1100 BE", 0, 1, 0 },
-	{ 0xffffffff, 0x03030200, "Au1550 AA", 0, 1, 0 },
-	{ 0xffffffff, 0x04030200, "Au1200 AB", 0, 0, 0 },
-	{ 0xffffffff, 0x04030201, "Au1200 AC", 1, 0, 0 },
-	{ 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0, 0 }
-};
-
-void set_cpuspec(void)
-{
-	struct	cpu_spec *sp;
-	u32	prid;
-
-	prid = read_c0_prid();
-	sp = cpu_specs;
-	while ((prid & sp->prid_mask) != sp->prid_value)
-		sp++;
-	cur_cpu_spec[0] = sp;
-}
diff --git a/arch/mips/au1000/common/setup.c b/arch/mips/au1000/common/setup.c
index 1ac6b06..8f06440 100644
--- a/arch/mips/au1000/common/setup.c
+++ b/arch/mips/au1000/common/setup.c
@@ -41,38 +41,14 @@ extern void __init board_setup(void);
 extern void au1000_restart(char *);
 extern void au1000_halt(void);
 extern void au1000_power_off(void);
-extern void set_cpuspec(void);
 
 void __init plat_mem_setup(void)
 {
-	struct	cpu_spec *sp;
 	char *argptr;
-	unsigned long prid, cpufreq, bclk;
-
-	set_cpuspec();
-	sp = cur_cpu_spec[0];
 
 	board_setup();  /* board specific setup */
 
-	prid = read_c0_prid();
-	if (sp->cpu_pll_wo)
-#ifdef CONFIG_SOC_AU1000_FREQUENCY
-		cpufreq = CONFIG_SOC_AU1000_FREQUENCY / 1000000;
-#else
-		cpufreq = 396;
-#endif
-	else
-		cpufreq = (au_readl(SYS_CPUPLL) & 0x3F) * 12;
-	printk(KERN_INFO "(PRID %08lx) @ %ld MHz\n", prid, cpufreq);
-
-	if (sp->cpu_bclk) {
-		/* Enable BCLK switching */
-		bclk = au_readl(SYS_POWERCTRL);
-		au_writel(bclk | 0x60, SYS_POWERCTRL);
-		printk(KERN_INFO "BCLK switching enabled!\n");
-	}
-
-	if (sp->cpu_od)
+	if (au1xxx_cpu_needs_config_od())
 		/* Various early Au1xx0 errata corrected by this */
 		set_c0_config(1 << 19); /* Set Config[OD] */
 	else
diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c
index 68d7142..1518570 100644
--- a/arch/mips/au1000/common/time.c
+++ b/arch/mips/au1000/common/time.c
@@ -198,7 +198,7 @@ unsigned long calc_clock(void)
 	 * silicon versions of Au1000 are not sold by AMD, we don't bend
 	 * over backwards trying to determine the frequency.
 	 */
-	if (cur_cpu_spec[0]->cpu_pll_wo)
+	if (au1xxx_cpu_has_pll_wo())
 #ifdef CONFIG_SOC_AU1000_FREQUENCY
 		cpu_speed = CONFIG_SOC_AU1000_FREQUENCY;
 #else
@@ -221,7 +221,7 @@ void __init plat_time_init(void)
 
 	est_freq += 5000;    /* round */
 	est_freq -= est_freq%10000;
-	printk(KERN_INFO "CPU frequency %u.%02u MHz\n",
+	printk(KERN_INFO "(PRId %08x) @ %u.%02u MHz\n", read_c0_prid(),
 	       est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
 	set_au1x00_speed(est_freq);
 
diff --git a/include/asm-mips/mach-au1x00/au1000.h b/include/asm-mips/mach-au1x00/au1000.h
index 8d2ced6..7245960 100644
--- a/include/asm-mips/mach-au1x00/au1000.h
+++ b/include/asm-mips/mach-au1x00/au1000.h
@@ -91,6 +91,44 @@ static inline u32 au_readl(unsigned long reg)
 	return *(volatile u32 *)reg;
 }
 
+/* Early Au1000 have a write-only SYS_CPUPLL register. */
+static inline int au1xxx_cpu_has_pll_wo(void)
+{
+	switch (read_c0_prid()) {
+	case 0x00030100:	/* Au1000 DA */
+	case 0x00030201:	/* Au1000 HA */
+	case 0x00030202:	/* Au1000 HB */
+		return 1;
+	}
+	return 0;
+}
+
+/* does CPU need CONFIG[OD] set to fix tons of errata? */
+static inline int au1xxx_cpu_needs_config_od(void)
+{
+	/*
+	 * c0_config.od (bit 19) was write only (and read as 0) on the
+	 * early revisions of Alchemy SOCs.  It disables the bus trans-
+	 * action overlapping and needs to be set to fix various errata.
+	 */
+	switch (read_c0_prid()) {
+	case 0x00030100: /* Au1000 DA */
+	case 0x00030201: /* Au1000 HA */
+	case 0x00030202: /* Au1000 HB */
+	case 0x01030200: /* Au1500 AB */
+	/*
+	 * Au1100/Au1200 errata actually keep silence about this bit,
+	 * so we set it just in case for those revisions that require
+	 * it to be set according to the (now gone) cpu_table.
+	 */
+	case 0x02030200: /* Au1100 AB */
+	case 0x02030201: /* Au1100 BA */
+	case 0x02030202: /* Au1100 BC */
+	case 0x04030201: /* Au1200 AC */
+		return 1;
+	}
+	return 0;
+}
 
 /* arch/mips/au1000/common/clocks.c */
 extern void set_au1x00_speed(unsigned int new_freq);
@@ -1747,24 +1785,4 @@ static AU1X00_SYS * const sys = (AU1X00_SYS *)SYS_BASE;
 
 #endif
 
-/*
- * Processor information based on PRID.
- * Copied from PowerPC.
- */
-#ifndef _LANGUAGE_ASSEMBLY
-struct cpu_spec {
-	/* CPU is matched via (PRID & prid_mask) == prid_value */
-	unsigned int	prid_mask;
-	unsigned int	prid_value;
-
-	char		*cpu_name;
-	unsigned char	cpu_od;		/* Set Config[OD] */
-	unsigned char	cpu_bclk;	/* Enable BCLK switching */
-	unsigned char	cpu_pll_wo;	/* sys_cpupll reg. write-only */
-};
-
-extern struct cpu_spec	cpu_specs[];
-extern struct cpu_spec	*cur_cpu_spec[];
-#endif
-
 #endif
-- 
1.5.6.3

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

* [PATCH v4 3/10] MIPS: make cp0 counter clocksource/event usable as fallback.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
  2008-07-29 16:59 ` [PATCH v4 1/10] Alchemy: remove get/set_au1x00_lcd_clock() Manuel Lauss
  2008-07-29 17:00 ` [PATCH v4 2/10] Alchemy: remove cpu_table Manuel Lauss
@ 2008-07-29 17:00 ` Manuel Lauss
  2008-07-29 17:01 ` [PATCH v4 4/10] Alchemy: RTC counter clocksource / clockevent support Manuel Lauss
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:00 UTC (permalink / raw)
  To: linux-mips

The current mips clock build infrastructure lets a system only use
either the MIPS cp0 counter or a SoC specific timer as a clocksource /
clockevent device.

This patch renames the core cp0 counter clocksource / clockevent functions
from mips_* to r4k_* and updates the wrappers in asm-mips/time.h to
call these renamed functions instead.

Chips which can detect whether it is safe to use a chip-specific timer
can now fall back on the cp0 counter if necessary and possible (e.g. au1xxx).

Existing behaviour is not changed in any way.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/Kconfig           |    8 ++++++++
 arch/mips/kernel/Makefile   |    4 ++--
 arch/mips/kernel/cevt-r4k.c |    2 +-
 arch/mips/kernel/csrc-r4k.c |    2 +-
 include/asm-mips/time.h     |   24 ++++++++++++++++--------
 5 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b4c4eaa..e585c77 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -690,7 +690,11 @@ config CEVT_DS1287
 config CEVT_GT641XX
 	bool
 
+config CEVT_R4K_LIB
+	bool
+
 config CEVT_R4K
+	select CEVT_R4K_LIB
 	bool
 
 config CEVT_SB1250
@@ -705,7 +709,11 @@ config CSRC_BCM1480
 config CSRC_IOASIC
 	bool
 
+config CSRC_R4K_LIB
+	bool
+
 config CSRC_R4K
+	select CSRC_R4K_LIB
 	bool
 
 config CSRC_SB1250
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 0fd3197..e8facef 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -9,14 +9,14 @@ obj-y		+= cpu-probe.o branch.o entry.o genex.o irq.o process.o \
 		   time.o topology.o traps.o unaligned.o
 
 obj-$(CONFIG_CEVT_BCM1480)	+= cevt-bcm1480.o
-obj-$(CONFIG_CEVT_R4K)		+= cevt-r4k.o
+obj-$(CONFIG_CEVT_R4K_LIB)	+= cevt-r4k.o
 obj-$(CONFIG_CEVT_DS1287)	+= cevt-ds1287.o
 obj-$(CONFIG_CEVT_GT641XX)	+= cevt-gt641xx.o
 obj-$(CONFIG_CEVT_SB1250)	+= cevt-sb1250.o
 obj-$(CONFIG_CEVT_TXX9)		+= cevt-txx9.o
 obj-$(CONFIG_CSRC_BCM1480)	+= csrc-bcm1480.o
 obj-$(CONFIG_CSRC_IOASIC)	+= csrc-ioasic.o
-obj-$(CONFIG_CSRC_R4K)		+= csrc-r4k.o
+obj-$(CONFIG_CSRC_R4K_LIB)	+= csrc-r4k.o
 obj-$(CONFIG_CSRC_SB1250)	+= csrc-sb1250.o
 obj-$(CONFIG_SYNC_R4K)		+= sync-r4k.o
 
diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
index 24a2d90..70f6343 100644
--- a/arch/mips/kernel/cevt-r4k.c
+++ b/arch/mips/kernel/cevt-r4k.c
@@ -219,7 +219,7 @@ static int c0_compare_int_usable(void)
 	return 1;
 }
 
-int __cpuinit mips_clockevent_init(void)
+int __cpuinit r4k_clockevent_init(void)
 {
 	uint64_t mips_freq = mips_hpt_frequency;
 	unsigned int cpu = smp_processor_id();
diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
index 86e026f..8dc1235 100644
--- a/arch/mips/kernel/csrc-r4k.c
+++ b/arch/mips/kernel/csrc-r4k.c
@@ -22,7 +22,7 @@ static struct clocksource clocksource_mips = {
 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-int __init init_mips_clocksource(void)
+int __init init_r4k_clocksource(void)
 {
 	if (!cpu_has_counter || !mips_hpt_frequency)
 		return -ENXIO;
diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h
index d3bd5c5..01a4c93 100644
--- a/include/asm-mips/time.h
+++ b/include/asm-mips/time.h
@@ -50,27 +50,35 @@ extern int (*perf_irq)(void);
 /*
  * Initialize the calling CPU's compare interrupt as clockevent device
  */
-#ifdef CONFIG_CEVT_R4K
-extern int mips_clockevent_init(void);
+#ifdef CONFIG_CEVT_R4K_LIB
 extern unsigned int __weak get_c0_compare_int(void);
-#else
+extern int r4k_clockevent_init(void);
+#endif
+
 static inline int mips_clockevent_init(void)
 {
+#ifdef CONFIG_CEVT_R4K
+	return r4k_clockevent_init();
+#else
 	return -ENXIO;
-}
 #endif
+}
 
 /*
  * Initialize the count register as a clocksource
  */
-#ifdef CONFIG_CEVT_R4K
-extern int init_mips_clocksource(void);
-#else
+#ifdef CONFIG_CSRC_R4K_LIB
+extern int init_r4k_clocksource(void);
+#endif
+
 static inline int init_mips_clocksource(void)
 {
+#ifdef CONFIG_CSRC_R4K
+	return init_r4k_clocksource(void);
+#else
 	return 0;
-}
 #endif
+}
 
 extern void clocksource_set_clock(struct clocksource *cs, unsigned int clock);
 extern void clockevent_set_clock(struct clock_event_device *cd,
-- 
1.5.6.3

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

* [PATCH v4 4/10] Alchemy: RTC counter clocksource / clockevent support.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (2 preceding siblings ...)
  2008-07-29 17:00 ` [PATCH v4 3/10] MIPS: make cp0 counter clocksource/event usable as fallback Manuel Lauss
@ 2008-07-29 17:01 ` Manuel Lauss
  2008-07-29 17:02 ` [PATCH v4 5/10] Alchemy: move calc_clock function Manuel Lauss
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:01 UTC (permalink / raw)
  To: linux-mips

Add support for the 32 kHz counter1 (RTC) as clocksource / clockevent
device.  As a nice side effect, this also enables use of the 'wait'
instruction for runtime idle power savings.

If the counters aren't enabled/working properly, fall back on the
cp0 counter clock code.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/Kconfig        |    4 +-
 arch/mips/au1000/common/power.c |   15 ++-
 arch/mips/au1000/common/setup.c |    6 -
 arch/mips/au1000/common/time.c  |  282 ++++++++++++++++-----------------------
 4 files changed, 131 insertions(+), 176 deletions(-)

diff --git a/arch/mips/au1000/Kconfig b/arch/mips/au1000/Kconfig
index 1fe97cc..c689cd6 100644
--- a/arch/mips/au1000/Kconfig
+++ b/arch/mips/au1000/Kconfig
@@ -128,8 +128,8 @@ config SOC_AU1200
 config SOC_AU1X00
 	bool
 	select 64BIT_PHYS_ADDR
-	select CEVT_R4K
-	select CSRC_R4K
+	select CEVT_R4K_LIB
+	select CSRC_R4K_LIB
 	select IRQ_CPU
 	select SYS_HAS_CPU_MIPS32_R1
 	select SYS_SUPPORTS_32BIT_KERNEL
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index bd854a6..462ab21 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -86,7 +86,11 @@ static unsigned int	sleep_static_memctlr[4][3];
 #define SLEEP_TEST_TIMEOUT 1
 #ifdef	SLEEP_TEST_TIMEOUT
 static int sleep_ticks;
-void wakeup_counter0_set(int ticks);
+static void wakeup_counter0_set(int ticks)
+{
+	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
+	au_sync();
+}
 #endif
 
 static void save_core_regs(void)
@@ -184,7 +188,6 @@ static void restore_core_regs(void)
 	}
 
 	restore_au1xxx_intctl();
-	wakeup_counter0_adjust();
 }
 
 unsigned long suspend_mode;
@@ -409,6 +412,14 @@ static struct ctl_table pm_dir_table[] = {
  */
 static int __init pm_init(void)
 {
+	/* init TOY to tick at 1Hz. No need to wait for access bits
+	 * since there's plenty of time between here and the first
+	 * suspend cycle.
+	 */
+	au_writel(32767, SYS_TOYTRIM);
+	au_writel(0, SYS_TOYWRITE);
+	au_sync();
+
 	register_sysctl_table(pm_dir_table);
 	return 0;
 }
diff --git a/arch/mips/au1000/common/setup.c b/arch/mips/au1000/common/setup.c
index 8f06440..b6242c6 100644
--- a/arch/mips/au1000/common/setup.c
+++ b/arch/mips/au1000/common/setup.c
@@ -89,12 +89,6 @@ void __init plat_mem_setup(void)
 	ioport_resource.end = IOPORT_RESOURCE_END;
 	iomem_resource.start = IOMEM_RESOURCE_START;
 	iomem_resource.end = IOMEM_RESOURCE_END;
-
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_E0S);
-	au_writel(SYS_CNTRL_E0 | SYS_CNTRL_EN0, SYS_COUNTER_CNTRL);
-	au_sync();
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S);
-	au_writel(0, SYS_TOYTRIM);
 }
 
 #if defined(CONFIG_64BIT_PHYS_ADDR)
diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c
index 1518570..fe859c1 100644
--- a/arch/mips/au1000/common/time.c
+++ b/arch/mips/au1000/common/time.c
@@ -23,131 +23,32 @@
  *
  * ########################################################################
  *
- * Setting up the clock on the MIPS boards.
+ * Clocksource/event using the Au1000 counter1.
+ * Compared to the C0 timer, the Au1000 counters are a very lousy clock-
+ * source and hrtimers are definitely unusable with them; but they
+ * enable the use of the 'wait' instructions for some power-savings
+ * during idle times.  Counter1 is used because unlike counter 0 (TOY)
+ * it doesn't continue counting during sleep/hibernate, which is perfectly
+ * acceptable.
  *
- * We provide the clock interrupt processing and the timer offset compute
- * functions.  If CONFIG_PM is selected, we also ensure the 32KHz timer is
- * available.  -- Dan
+ * CP0 counter clocksource will be used if no 32kHz source is available.
  */
 
-#include <linux/types.h>
-#include <linux/init.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
 #include <linux/spinlock.h>
 
-#include <asm/mipsregs.h>
 #include <asm/time.h>
 #include <asm/mach-au1x00/au1000.h>
 
-static int no_au1xxx_32khz;
-extern int allow_au1k_wait; /* default off for CP0 Counter */
+/* 32kHz clock enabled and detected */
+#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
 
-#ifdef CONFIG_PM
-#if HZ < 100 || HZ > 1000
-#error "unsupported HZ value! Must be in [100,1000]"
-#endif
-#define MATCH20_INC (328 * 100 / HZ) /* magic number 328 is for HZ=100... */
-static unsigned long last_pc0, last_match20;
-#endif
+extern int allow_au1k_wait; /* default off for CP0 Counter */
 
 static DEFINE_SPINLOCK(time_lock);
 
-unsigned long wtimer;
-
-#ifdef CONFIG_PM
-static irqreturn_t counter0_irq(int irq, void *dev_id)
-{
-	unsigned long pc0;
-	int time_elapsed;
-	static int jiffie_drift;
-
-	if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {
-		/* should never happen! */
-		printk(KERN_WARNING "counter 0 w status error\n");
-		return IRQ_NONE;
-	}
-
-	pc0 = au_readl(SYS_TOYREAD);
-	if (pc0 < last_match20)
-		/* counter overflowed */
-		time_elapsed = (0xffffffff - last_match20) + pc0;
-	else
-		time_elapsed = pc0 - last_match20;
-
-	while (time_elapsed > 0) {
-		do_timer(1);
-#ifndef CONFIG_SMP
-		update_process_times(user_mode(get_irq_regs()));
-#endif
-		time_elapsed -= MATCH20_INC;
-		last_match20 += MATCH20_INC;
-		jiffie_drift++;
-	}
-
-	last_pc0 = pc0;
-	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-	au_sync();
-
-	/*
-	 * Our counter ticks at 10.009765625 ms/tick, we we're running
-	 * almost 10 uS too slow per tick.
-	 */
-
-	if (jiffie_drift >= 999) {
-		jiffie_drift -= 999;
-		do_timer(1); /* increment jiffies by one */
-#ifndef CONFIG_SMP
-		update_process_times(user_mode(get_irq_regs()));
-#endif
-	}
-
-	return IRQ_HANDLED;
-}
-
-struct irqaction counter0_action = {
-	.handler	= counter0_irq,
-	.flags		= IRQF_DISABLED,
-	.name		= "alchemy-toy",
-	.dev_id		= NULL,
-};
-
-/* When we wakeup from sleep, we have to "catch up" on all of the
- * timer ticks we have missed.
- */
-void wakeup_counter0_adjust(void)
-{
-	unsigned long pc0;
-	int time_elapsed;
-
-	pc0 = au_readl(SYS_TOYREAD);
-	if (pc0 < last_match20)
-		/* counter overflowed */
-		time_elapsed = (0xffffffff - last_match20) + pc0;
-	else
-		time_elapsed = pc0 - last_match20;
-
-	while (time_elapsed > 0) {
-		time_elapsed -= MATCH20_INC;
-		last_match20 += MATCH20_INC;
-	}
-
-	last_pc0 = pc0;
-	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-	au_sync();
-
-}
-
-/* This is just for debugging to set the timer for a sleep delay. */
-void wakeup_counter0_set(int ticks)
-{
-	unsigned long pc0;
-
-	pc0 = au_readl(SYS_TOYREAD);
-	last_pc0 = pc0;
-	au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2);
-	au_sync();
-}
-#endif
-
 /*
  * I haven't found anyone that doesn't use a 12 MHz source clock,
  * but just in case.....
@@ -162,37 +63,15 @@ void wakeup_counter0_set(int ticks)
  * this advertised speed will introduce error and sometimes not work
  * properly.  This function is futher convoluted to still allow configurations
  * to do that in case they have really, really old silicon with a
- * write-only PLL register, that we need the 32 KHz when power management
- * "wait" is enabled, and we need to detect if the 32 KHz isn't present
- * but requested......got it? :-)		-- Dan
+ * write-only PLL register.			-- Dan
  */
 unsigned long calc_clock(void)
 {
 	unsigned long cpu_speed;
 	unsigned long flags;
-	unsigned long counter;
 
 	spin_lock_irqsave(&time_lock, flags);
 
-	/* Power management cares if we don't have a 32 KHz counter. */
-	no_au1xxx_32khz = 0;
-	counter = au_readl(SYS_COUNTER_CNTRL);
-	if (counter & SYS_CNTRL_E0) {
-		int trim_divide = 16;
-
-		au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
-
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-		/* RTC now ticks at 32.768/16 kHz */
-		au_writel(trim_divide - 1, SYS_RTCTRIM);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
-		au_writel(0, SYS_TOYWRITE);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
-	} else
-		no_au1xxx_32khz = 1;
-
 	/*
 	 * On early Au1000, sys_cpupll was write-only. Since these
 	 * silicon versions of Au1000 are not sold by AMD, we don't bend
@@ -215,8 +94,60 @@ unsigned long calc_clock(void)
 	return cpu_speed;
 }
 
+static cycle_t au1x_counter1_read(void)
+{
+	return au_readl(SYS_RTCREAD);
+}
+
+static struct clocksource au1x_counter1_clocksource = {
+	.name		= "au1000-counter1",
+	.read		= au1x_counter1_read,
+	.mask		= CLOCKSOURCE_MASK(32),
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+	.rating		= 100,
+};
+
+static int au1x_rtcmatch2_set_next_event(unsigned long delta,
+					 struct clock_event_device *cd)
+{
+	au_writel(au_readl(SYS_RTCREAD) + delta, SYS_RTCMATCH2);
+	au_sync();
+	return 0;
+}
+
+static void au1x_rtcmatch2_set_mode(enum clock_event_mode mode,
+				    struct clock_event_device *cd)
+{
+}
+
+static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id)
+{
+	struct clock_event_device *cd = dev_id;
+	cd->event_handler(cd);
+	return IRQ_HANDLED;
+}
+
+static struct clock_event_device au1x_rtcmatch2_clockdev = {
+	.name		= "rtcmatch2",
+	.features	= CLOCK_EVT_FEAT_ONESHOT,
+	.rating		= 100,
+	.irq		= AU1000_RTC_MATCH2_INT,
+	.set_next_event	= au1x_rtcmatch2_set_next_event,
+	.set_mode	= au1x_rtcmatch2_set_mode,
+	.cpumask	= CPU_MASK_ALL,
+};
+
+static struct irqaction au1x_rtcmatch2_irqaction = {
+	.handler	= au1x_rtcmatch2_irq,
+	.flags		= IRQF_DISABLED,
+	.name		= "rtcmatch2",
+	.dev_id		= &au1x_rtcmatch2_clockdev,
+};
+
 void __init plat_time_init(void)
 {
+	struct clock_event_device *cd = &au1x_rtcmatch2_clockdev;
+	unsigned long t;
 	unsigned int est_freq = calc_clock();
 
 	est_freq += 5000;    /* round */
@@ -225,41 +156,60 @@ void __init plat_time_init(void)
 	       est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
 	set_au1x00_speed(est_freq);
 
-#ifdef CONFIG_PM
-	/*
-	 * setup counter 0, since it keeps ticking after a
-	 * 'wait' instruction has been executed. The CP0 timer and
-	 * counter 1 do NOT continue running after 'wait'
-	 *
-	 * It's too early to call request_irq() here, so we handle
-	 * counter 0 interrupt as a special irq and it doesn't show
-	 * up under /proc/interrupts.
-	 *
-	 * Check to ensure we really have a 32 KHz oscillator before
-	 * we do this.
+	/* Check if firmware (YAMON, ...) has enabled 32kHz and clock
+	 * has been detected.  If so install the rtcmatch2 clocksource,
+	 * otherwise don't bother.  Note that both bits being set is by
+	 * no means a definite guarantee that the counters actually work
+	 * (the 32S bit seems to be stuck set to 1 once a single clock-
+	 * edge is detected, hence the timeouts).
 	 */
-	if (no_au1xxx_32khz)
-		printk(KERN_WARNING "WARNING: no 32KHz clock found.\n");
-	else {
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
-		au_writel(0, SYS_TOYWRITE);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
-
-		au_writel(au_readl(SYS_WAKEMSK) | (1 << 8), SYS_WAKEMSK);
-		au_writel(~0, SYS_WAKESRC);
+	if (CNTR_OK == (au_readl(SYS_COUNTER_CNTRL) & CNTR_OK)) {
+		/*
+		 * setup counter 1 (RTC) to tick at full speed
+		 */
+		t = 0xffffff;
+		while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S) && t--)
+			asm volatile ("nop");
+		if (!t)
+			goto cntr_err;
+
+		au_writel(0, SYS_RTCTRIM);	/* 32.768 kHz */
 		au_sync();
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
 
-		/* Setup match20 to interrupt once every HZ */
-		last_pc0 = last_match20 = au_readl(SYS_TOYREAD);
-		au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
+		t = 0xffffff;
+		while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--)
+			asm volatile ("nop");
+		if (!t)
+			goto cntr_err;
+		au_writel(0, SYS_RTCWRITE);
 		au_sync();
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
-		setup_irq(AU1000_TOY_MATCH2_INT, &counter0_action);
 
-		/* We can use the real 'wait' instruction. */
+		t = 0xffffff;
+		while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--)
+			asm volatile ("nop");
+		if (!t)
+			goto cntr_err;
+
+		/* register counter1 clocksource and event device */
+		cd->mult = div_sc(32768, NSEC_PER_SEC, 32);
+		cd->shift = 32;
+		cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+		cd->min_delta_ns = clockevent_delta2ns(0x140, cd);
+		clockevents_register_device(cd);
+		setup_irq(AU1000_RTC_MATCH2_INT, &au1x_rtcmatch2_irqaction);
+
+		clocksource_set_clock(&au1x_counter1_clocksource, 32768);
+		clocksource_register(&au1x_counter1_clocksource);
+
+		printk(KERN_INFO "Au1x RTCMATCH2 clocksource installed\n");
+
+		/* can now use 'wait' */
 		allow_au1k_wait = 1;
+	} else {
+cntr_err:
+		/* counters unusable, use CP0 counter */
+		r4k_clockevent_init();
+		init_r4k_clocksource();
+		allow_au1k_wait = 0;
 	}
-
-#endif
 }
-- 
1.5.6.3

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

* [PATCH v4 5/10] Alchemy: move calc_clock function.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (3 preceding siblings ...)
  2008-07-29 17:01 ` [PATCH v4 4/10] Alchemy: RTC counter clocksource / clockevent support Manuel Lauss
@ 2008-07-29 17:02 ` Manuel Lauss
  2008-07-29 17:02 ` [PATCH v4 6/10] Alchemy: compile platform.c only when building for a demoboard Manuel Lauss
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:02 UTC (permalink / raw)
  To: linux-mips

Now that nothing in time.c depends on calc_clock, it can
be moved to clocks.c where it belongs.
While at it, give it a better non-generic name and call it
as soon as possible in plat_mem_init.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/clocks.c |   54 ++++++++++++++++++++++++++++++++++++++
 arch/mips/au1000/common/dbg_io.c |    4 +-
 arch/mips/au1000/common/setup.c  |    9 ++++++
 arch/mips/au1000/common/time.c   |   54 --------------------------------------
 4 files changed, 65 insertions(+), 56 deletions(-)

diff --git a/arch/mips/au1000/common/clocks.c b/arch/mips/au1000/common/clocks.c
index a8170fd..d899185 100644
--- a/arch/mips/au1000/common/clocks.c
+++ b/arch/mips/au1000/common/clocks.c
@@ -27,11 +27,21 @@
  */
 
 #include <linux/module.h>
+#include <linux/spinlock.h>
+#include <asm/time.h>
 #include <asm/mach-au1x00/au1000.h>
 
+/*
+ * I haven't found anyone that doesn't use a 12 MHz source clock,
+ * but just in case.....
+ */
+#define AU1000_SRC_CLK	12000000
+
 static unsigned int au1x00_clock; /*  Hz */
 static unsigned long uart_baud_base;
 
+static DEFINE_SPINLOCK(time_lock);
+
 /*
  * Set the au1000_clock
  */
@@ -60,3 +70,47 @@ void set_au1x00_uart_baud_base(unsigned long new_baud_base)
 {
 	uart_baud_base = new_baud_base;
 }
+
+/*
+ * We read the real processor speed from the PLL.  This is important
+ * because it is more accurate than computing it from the 32 KHz
+ * counter, if it exists.  If we don't have an accurate processor
+ * speed, all of the peripherals that derive their clocks based on
+ * this advertised speed will introduce error and sometimes not work
+ * properly.  This function is futher convoluted to still allow configurations
+ * to do that in case they have really, really old silicon with a
+ * write-only PLL register.			-- Dan
+ */
+unsigned long au1xxx_calc_clock(void)
+{
+	unsigned long cpu_speed;
+	unsigned long flags;
+
+	spin_lock_irqsave(&time_lock, flags);
+
+	/*
+	 * On early Au1000, sys_cpupll was write-only. Since these
+	 * silicon versions of Au1000 are not sold by AMD, we don't bend
+	 * over backwards trying to determine the frequency.
+	 */
+	if (au1xxx_cpu_has_pll_wo())
+#ifdef CONFIG_SOC_AU1000_FREQUENCY
+		cpu_speed = CONFIG_SOC_AU1000_FREQUENCY;
+#else
+		cpu_speed = 396000000;
+#endif
+	else
+		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK;
+
+	/* On Alchemy CPU:counter ratio is 1:1 */
+	mips_hpt_frequency = cpu_speed;
+	/* Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) */
+	set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)
+							  & 0x03) + 2) * 16));
+
+	spin_unlock_irqrestore(&time_lock, flags);
+
+	set_au1x00_speed(cpu_speed);
+
+	return cpu_speed;
+}
diff --git a/arch/mips/au1000/common/dbg_io.c b/arch/mips/au1000/common/dbg_io.c
index af5be7d..72abb67 100644
--- a/arch/mips/au1000/common/dbg_io.c
+++ b/arch/mips/au1000/common/dbg_io.c
@@ -49,13 +49,13 @@
 #define UART16550_READ(y)     (au_readl(DEBUG_BASE + y) & 0xff)
 #define UART16550_WRITE(y, z) (au_writel(z & 0xff, DEBUG_BASE + y))
 
-extern unsigned long calc_clock(void);
+extern unsigned long au1xxx_calc_clock(void);
 
 void debugInit(u32 baud, u8 data, u8 parity, u8 stop)
 {
 	if (UART16550_READ(UART_MOD_CNTRL) != 0x3)
 		UART16550_WRITE(UART_MOD_CNTRL, 3);
-	calc_clock();
+	au1xxx_calc_clock();
 
 	/* disable interrupts */
 	UART16550_WRITE(UART_IER, 0);
diff --git a/arch/mips/au1000/common/setup.c b/arch/mips/au1000/common/setup.c
index b6242c6..c5bd251 100644
--- a/arch/mips/au1000/common/setup.c
+++ b/arch/mips/au1000/common/setup.c
@@ -41,10 +41,19 @@ extern void __init board_setup(void);
 extern void au1000_restart(char *);
 extern void au1000_halt(void);
 extern void au1000_power_off(void);
+extern unsigned long au1xxx_calc_clock(void);
 
 void __init plat_mem_setup(void)
 {
 	char *argptr;
+	unsigned long est_freq;
+
+	/* determine core clock */
+	est_freq = au1xxx_calc_clock();
+	est_freq += 5000;    /* round */
+	est_freq -= est_freq % 10000;
+	printk(KERN_INFO "(PRId %08x) @ %lu.%02lu MHz\n", read_c0_prid(),
+	       est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
 
 	board_setup();  /* board specific setup */
 
diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c
index fe859c1..0de9a43 100644
--- a/arch/mips/au1000/common/time.c
+++ b/arch/mips/au1000/common/time.c
@@ -47,53 +47,6 @@
 
 extern int allow_au1k_wait; /* default off for CP0 Counter */
 
-static DEFINE_SPINLOCK(time_lock);
-
-/*
- * I haven't found anyone that doesn't use a 12 MHz source clock,
- * but just in case.....
- */
-#define AU1000_SRC_CLK	12000000
-
-/*
- * We read the real processor speed from the PLL.  This is important
- * because it is more accurate than computing it from the 32 KHz
- * counter, if it exists.  If we don't have an accurate processor
- * speed, all of the peripherals that derive their clocks based on
- * this advertised speed will introduce error and sometimes not work
- * properly.  This function is futher convoluted to still allow configurations
- * to do that in case they have really, really old silicon with a
- * write-only PLL register.			-- Dan
- */
-unsigned long calc_clock(void)
-{
-	unsigned long cpu_speed;
-	unsigned long flags;
-
-	spin_lock_irqsave(&time_lock, flags);
-
-	/*
-	 * On early Au1000, sys_cpupll was write-only. Since these
-	 * silicon versions of Au1000 are not sold by AMD, we don't bend
-	 * over backwards trying to determine the frequency.
-	 */
-	if (au1xxx_cpu_has_pll_wo())
-#ifdef CONFIG_SOC_AU1000_FREQUENCY
-		cpu_speed = CONFIG_SOC_AU1000_FREQUENCY;
-#else
-		cpu_speed = 396000000;
-#endif
-	else
-		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK;
-	/* On Alchemy CPU:counter ratio is 1:1 */
-	mips_hpt_frequency = cpu_speed;
-	/* Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) */
-	set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)
-							  & 0x03) + 2) * 16));
-	spin_unlock_irqrestore(&time_lock, flags);
-	return cpu_speed;
-}
-
 static cycle_t au1x_counter1_read(void)
 {
 	return au_readl(SYS_RTCREAD);
@@ -148,13 +101,6 @@ void __init plat_time_init(void)
 {
 	struct clock_event_device *cd = &au1x_rtcmatch2_clockdev;
 	unsigned long t;
-	unsigned int est_freq = calc_clock();
-
-	est_freq += 5000;    /* round */
-	est_freq -= est_freq%10000;
-	printk(KERN_INFO "(PRId %08x) @ %u.%02u MHz\n", read_c0_prid(),
-	       est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000);
-	set_au1x00_speed(est_freq);
 
 	/* Check if firmware (YAMON, ...) has enabled 32kHz and clock
 	 * has been detected.  If so install the rtcmatch2 clocksource,
-- 
1.5.6.3

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

* [PATCH v4 6/10] Alchemy: compile platform.c only when building for a demoboard.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (4 preceding siblings ...)
  2008-07-29 17:02 ` [PATCH v4 5/10] Alchemy: move calc_clock function Manuel Lauss
@ 2008-07-29 17:02 ` Manuel Lauss
  2008-07-29 17:03 ` [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts Manuel Lauss
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:02 UTC (permalink / raw)
  To: linux-mips

The platform.c registers devices not all boards might need/want,
and it makes adding platform data to drivers needlessly ugly and
complicated.

Most (all?) in-kernel users of Au1xxx are demoboards, and the file
in question somewhat reflects that.  Remove the platform.c from the
common Alchemy Makefile and add it to the Makefiles of the boards.
Existing behavior of all in-kernel boards is not changed in any way.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/Makefile   |    2 +-
 arch/mips/au1000/common/platform.c |    7 ++++++-
 arch/mips/au1000/db1x00/Makefile   |    1 +
 arch/mips/au1000/mtx-1/Makefile    |    2 +-
 arch/mips/au1000/pb1000/Makefile   |    1 +
 arch/mips/au1000/pb1100/Makefile   |    1 +
 arch/mips/au1000/pb1200/Makefile   |    2 +-
 arch/mips/au1000/pb1500/Makefile   |    1 +
 arch/mips/au1000/pb1550/Makefile   |    1 +
 arch/mips/au1000/xxs1500/Makefile  |    1 +
 10 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/arch/mips/au1000/common/Makefile b/arch/mips/au1000/common/Makefile
index 850de08..aefb2b8 100644
--- a/arch/mips/au1000/common/Makefile
+++ b/arch/mips/au1000/common/Makefile
@@ -6,7 +6,7 @@
 #
 
 obj-y += prom.o irq.o puts.o time.o reset.o \
-	au1xxx_irqmap.o clocks.o platform.o power.o setup.o \
+	au1xxx_irqmap.o clocks.o power.o setup.o \
 	sleeper.o dma.o dbdma.o gpio.o
 
 obj-$(CONFIG_KGDB)		+= dbg_io.o
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
index dc8a67e..66d6770 100644
--- a/arch/mips/au1000/common/platform.c
+++ b/arch/mips/au1000/common/platform.c
@@ -1,5 +1,5 @@
 /*
- * Platform device support for Au1x00 SoCs.
+ * Common device support for Au1x00 demoboards.
  *
  * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
  *
@@ -9,6 +9,11 @@
  * This file is licensed under the terms of the GNU General Public
  * License version 2.  This program is licensed "as is" without any
  * warranty of any kind, whether express or implied.
+ *
+ * This file is intended to be included by all db1xxx/pb1xxx demoboards,
+ * and is currently used by all the in-tree ones.  If your system does
+ * not want/need all the devices registered in here (like mine) then
+ * simply don't include it in your Makefile ;-)
  */
 
 #include <linux/dma-mapping.h>
diff --git a/arch/mips/au1000/db1x00/Makefile b/arch/mips/au1000/db1x00/Makefile
index 274db3b..3bfec10 100644
--- a/arch/mips/au1000/db1x00/Makefile
+++ b/arch/mips/au1000/db1x00/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
diff --git a/arch/mips/au1000/mtx-1/Makefile b/arch/mips/au1000/mtx-1/Makefile
index 7c67b3d..ef98975 100644
--- a/arch/mips/au1000/mtx-1/Makefile
+++ b/arch/mips/au1000/mtx-1/Makefile
@@ -7,6 +7,6 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
-obj-y := platform.o
+obj-y := platform.o ../common/platform.o
 
 EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/au1000/pb1000/Makefile b/arch/mips/au1000/pb1000/Makefile
index 99bbec0..41a09be 100644
--- a/arch/mips/au1000/pb1000/Makefile
+++ b/arch/mips/au1000/pb1000/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
diff --git a/arch/mips/au1000/pb1100/Makefile b/arch/mips/au1000/pb1100/Makefile
index 793e97c..1a8cd71 100644
--- a/arch/mips/au1000/pb1100/Makefile
+++ b/arch/mips/au1000/pb1100/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
diff --git a/arch/mips/au1000/pb1200/Makefile b/arch/mips/au1000/pb1200/Makefile
index d678adf..7b9f2f5 100644
--- a/arch/mips/au1000/pb1200/Makefile
+++ b/arch/mips/au1000/pb1200/Makefile
@@ -3,6 +3,6 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
-obj-y += platform.o
+obj-y += platform.o ../common/platform.o
 
 EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/au1000/pb1500/Makefile b/arch/mips/au1000/pb1500/Makefile
index 602f38d..c12e1bd 100644
--- a/arch/mips/au1000/pb1500/Makefile
+++ b/arch/mips/au1000/pb1500/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
diff --git a/arch/mips/au1000/pb1550/Makefile b/arch/mips/au1000/pb1550/Makefile
index 7d8beca..8df0890 100644
--- a/arch/mips/au1000/pb1550/Makefile
+++ b/arch/mips/au1000/pb1550/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
diff --git a/arch/mips/au1000/xxs1500/Makefile b/arch/mips/au1000/xxs1500/Makefile
index db3c526..86db0f7 100644
--- a/arch/mips/au1000/xxs1500/Makefile
+++ b/arch/mips/au1000/xxs1500/Makefile
@@ -6,3 +6,4 @@
 #
 
 lib-y := init.o board_setup.o irqmap.o
+obj-y := ../common/platform.o
-- 
1.5.6.3

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

* [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (5 preceding siblings ...)
  2008-07-29 17:02 ` [PATCH v4 6/10] Alchemy: compile platform.c only when building for a demoboard Manuel Lauss
@ 2008-07-29 17:03 ` Manuel Lauss
  2008-07-30  7:42   ` Dmitri Vorobiev
  2008-07-29 17:04 ` [PATCH v4 8/10] Alchemy: Fix PM code for Au1200/Au1550 Manuel Lauss
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:03 UTC (permalink / raw)
  To: linux-mips

The Alchemy power.c file contains both core suspend/resume code, which
is processor specific, and leftovers of the 2.4 PM userspace interface
(sysctls).

This patch moves the userspace interface to the platform.c file and
leaves the core board-independent suspend/resume parts which should be
usable for all Alchemy-based systems intact.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/platform.c |  304 ++++++++++++++++++++++++++++++++++-
 arch/mips/au1000/common/power.c    |  317 +-----------------------------------
 2 files changed, 305 insertions(+), 316 deletions(-)

diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
index 66d6770..1e89560 100644
--- a/arch/mips/au1000/common/platform.c
+++ b/arch/mips/au1000/common/platform.c
@@ -6,6 +6,9 @@
  * (C) Copyright Embedded Alley Solutions, Inc 2005
  * Author: Pantelis Antoniou <pantelis@embeddedalley.com>
  *
+ *  Some of the routines are right out of init/main.c, whose
+ *  copyrights apply here.
+ *
  * This file is licensed under the terms of the GNU General Public
  * License version 2.  This program is licensed "as is" without any
  * warranty of any kind, whether express or implied.
@@ -19,7 +22,11 @@
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
-#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/spinlock.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
 
 #include <asm/mach-au1x00/au1xxx.h>
 
@@ -322,3 +329,298 @@ static int __init au1xxx_platform_init(void)
 }
 
 arch_initcall(au1xxx_platform_init);
+
+
+/*********************************************************************/
+
+
+#ifdef CONFIG_PM
+
+static DEFINE_SPINLOCK(pm_lock);
+
+extern unsigned long save_local_and_disable(int controller);
+extern void restore_local_and_enable(int controller, unsigned long mask);
+extern void local_enable_irq(unsigned int irq_nr);
+extern void au_sleep(void);
+
+/*
+ * Define this to cause the value you write to /proc/sys/pm/sleep to
+ * set the TOY timer for the amount of time you want to sleep.
+ * This is done mainly for testing, but may be useful in other cases.
+ * The value is number of 32KHz ticks to sleep.
+ */
+#define SLEEP_TEST_TIMEOUT 1
+#ifdef	SLEEP_TEST_TIMEOUT
+static int sleep_ticks;
+static void wakeup_counter0_set(int ticks)
+{
+	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
+	au_sync();
+}
+#endif
+
+static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
+		       void __user *buffer, size_t *len, loff_t *ppos)
+{
+	unsigned long wakeup, flags;
+#ifdef SLEEP_TEST_TIMEOUT
+#define TMPBUFLEN2 16
+	char buf[TMPBUFLEN2], *p;
+#endif
+
+	spin_lock_irqsave(&pm_lock, flags);
+
+	if (!write) {
+		*len = 0;
+		spin_unlock_irqrestore(&pm_lock, flags);
+		return 0;
+	}
+
+#ifdef SLEEP_TEST_TIMEOUT
+	if (*len > TMPBUFLEN2 - 1)
+		return -EFAULT;
+	if (copy_from_user(buf, buffer, *len))
+		return -EFAULT;
+	buf[*len] = 0;
+	p = buf;
+	sleep_ticks = simple_strtoul(p, &p, 0);
+#endif
+
+	/**
+	 ** The code below is all system dependent and we should probably
+	 ** have a function call out of here to set this up.  You need
+	 ** to configure the GPIO or timer interrupts that will bring
+	 ** you out of sleep.
+	 ** For testing, the TOY counter wakeup is useful.
+	 **/
+#if 0
+	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
+
+	/* GPIO 6 can cause a wake up event */
+	wakeup = au_readl(SYS_WAKEMSK);
+	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
+	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
+#else
+	/* For testing, allow match20 to wake us up. */
+#ifdef SLEEP_TEST_TIMEOUT
+	wakeup_counter0_set(sleep_ticks);
+#endif
+	wakeup = 1 << 8;	/* turn on match20 wakeup   */
+#endif
+	au_writel(1, SYS_WAKESRC);	/* clear cause */
+	au_sync();
+	au_writel(wakeup, SYS_WAKEMSK);
+	au_sync();
+
+	au_sleep();
+
+	spin_unlock_irqrestore(&pm_lock, flags);
+
+	return 0;
+}
+
+/*
+ * This is the number of bits of precision for the loops_per_jiffy.
+ * Each bit takes on average 1.5/HZ seconds.  This (like the original)
+ * is a little better than 1%.
+ */
+#define LPS_PREC 8
+
+static void au1000_calibrate_delay(void)
+{
+	unsigned long ticks, loopbit;
+	int lps_precision = LPS_PREC;
+
+	loops_per_jiffy = 1 << 12;
+
+	while (loops_per_jiffy <<= 1) {
+		/* Wait for "start of" clock tick */
+		ticks = jiffies;
+		while (ticks == jiffies)
+			/* nothing */ ;
+		/* Go ... */
+		ticks = jiffies;
+		__delay(loops_per_jiffy);
+		ticks = jiffies - ticks;
+		if (ticks)
+			break;
+	}
+
+	/*
+	 * Do a binary approximation to get loops_per_jiffy set to be equal
+	 * one clock (up to lps_precision bits)
+	 */
+	loops_per_jiffy >>= 1;
+	loopbit = loops_per_jiffy;
+	while (lps_precision-- && (loopbit >>= 1)) {
+		loops_per_jiffy |= loopbit;
+		ticks = jiffies;
+		while (ticks == jiffies);
+		ticks = jiffies;
+		__delay(loops_per_jiffy);
+		if (jiffies != ticks)	/* longer than 1 tick */
+			loops_per_jiffy &= ~loopbit;
+	}
+}
+
+static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
+		      void __user *buffer, size_t *len, loff_t *ppos)
+{
+	int retval = 0, i;
+	unsigned long val, pll;
+#define TMPBUFLEN 64
+#define MAX_CPU_FREQ 396
+	char buf[TMPBUFLEN], *p;
+	unsigned long flags, intc0_mask, intc1_mask;
+	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
+	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
+	unsigned long baud_rate;
+
+	spin_lock_irqsave(&pm_lock, flags);
+	if (!write)
+		*len = 0;
+	else {
+		/* Parse the new frequency */
+		if (*len > TMPBUFLEN - 1) {
+			spin_unlock_irqrestore(&pm_lock, flags);
+			return -EFAULT;
+		}
+		if (copy_from_user(buf, buffer, *len)) {
+			spin_unlock_irqrestore(&pm_lock, flags);
+			return -EFAULT;
+		}
+		buf[*len] = 0;
+		p = buf;
+		val = simple_strtoul(p, &p, 0);
+		if (val > MAX_CPU_FREQ) {
+			spin_unlock_irqrestore(&pm_lock, flags);
+			return -EFAULT;
+		}
+
+		pll = val / 12;
+		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
+			/* Revisit this for higher speed CPUs */
+			spin_unlock_irqrestore(&pm_lock, flags);
+			return -EFAULT;
+		}
+
+		old_baud_base = get_au1x00_uart_baud_base();
+		old_cpu_freq = get_au1x00_speed();
+
+		new_cpu_freq = pll * 12 * 1000000;
+	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
+							    & 0x03) + 2) * 16));
+		set_au1x00_speed(new_cpu_freq);
+		set_au1x00_uart_baud_base(new_baud_base);
+
+		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
+		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
+			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
+
+		au_writel(pll, SYS_CPUPLL);
+		au_sync_delay(1);
+		au_writel(new_refresh, MEM_SDREFCFG);
+		au_sync_delay(1);
+
+		for (i = 0; i < 4; i++)
+			if (au_readl(UART_BASE + UART_MOD_CNTRL +
+				     i * 0x00100000) == 3) {
+				old_clk = au_readl(UART_BASE + UART_CLK +
+						   i * 0x00100000);
+				baud_rate = old_baud_base / old_clk;
+				/*
+				 * We won't get an exact baud rate and the error
+				 * could be significant enough that our new
+				 * calculation will result in a clock that will
+				 * give us a baud rate that's too far off from
+				 * what we really want.
+				 */
+				if (baud_rate > 100000)
+					baud_rate = 115200;
+				else if (baud_rate > 50000)
+					baud_rate = 57600;
+				else if (baud_rate > 30000)
+					baud_rate = 38400;
+				else if (baud_rate > 17000)
+					baud_rate = 19200;
+				else
+					baud_rate = 9600;
+				new_clk = new_baud_base / baud_rate;
+				au_writel(new_clk, UART_BASE + UART_CLK +
+					  i * 0x00100000);
+				au_sync_delay(10);
+			}
+	}
+
+	/*
+	 * We don't want _any_ interrupts other than match20. Otherwise our
+	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
+	 */
+	intc0_mask = save_local_and_disable(0);
+	intc1_mask = save_local_and_disable(1);
+	local_enable_irq(AU1000_TOY_MATCH2_INT);
+	spin_unlock_irqrestore(&pm_lock, flags);
+	au1000_calibrate_delay();
+	restore_local_and_enable(0, intc0_mask);
+	restore_local_and_enable(1, intc1_mask);
+
+	return retval;
+}
+
+
+static struct ctl_table pm_table[] = {
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "sleep",
+		.data		= NULL,
+		.maxlen		= 0,
+		.mode		= 0600,
+		.proc_handler	= &pm_do_sleep
+	},
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "freq",
+		.data		= NULL,
+		.maxlen		= 0,
+		.mode		= 0600,
+		.proc_handler	= &pm_do_freq
+	},
+	{}
+};
+
+static struct ctl_table pm_dir_table[] = {
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "pm",
+		.mode		= 0555,
+		.child		= pm_table
+	},
+	{}
+};
+
+/*
+ * Initialize power interface
+ */
+static int __init pm_init(void)
+{
+	/* init TOY to tick at 1Hz. No need to wait for access bits
+	 * since there's plenty of time between here and the first
+	 * suspend cycle.
+	 */
+	au_writel(32767, SYS_TOYTRIM);
+	au_writel(0, SYS_TOYWRITE);
+	au_sync();
+
+	au_writel(0, SYS_WAKESRC);
+	au_sync();
+	au_writel(0, SYS_WAKEMSK);
+	au_sync();
+
+	register_sysctl_table(pm_dir_table);
+
+	return 0;
+}
+
+__initcall(pm_init);
+
+#endif	/* CONFIG_PM */
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index 462ab21..4b0f6a1 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -5,9 +5,6 @@
  * Copyright 2001, 2008 MontaVista Software Inc.
  * Author: MontaVista Software, Inc. <source@mvista.com>
  *
- *  Some of the routines are right out of init/main.c, whose
- *  copyrights apply here.
- *
  *  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 of the	License, or (at your
@@ -30,30 +27,12 @@
  */
 
 #include <linux/init.h>
-#include <linux/pm.h>
-#include <linux/sysctl.h>
-#include <linux/jiffies.h>
-
-#include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <asm/mach-au1x00/au1000.h>
 
 #ifdef CONFIG_PM
 
-#define DEBUG 1
-#ifdef	DEBUG
-#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __func__, ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-static void au1000_calibrate_delay(void);
-
-extern unsigned long save_local_and_disable(int controller);
-extern void restore_local_and_enable(int controller, unsigned long mask);
-extern void local_enable_irq(unsigned int irq_nr);
-
-static DEFINE_SPINLOCK(pm_lock);
+extern void save_and_sleep(void);
 
 /*
  * We need to save/restore a bunch of core registers that are
@@ -77,22 +56,6 @@ static unsigned int	sleep_usbhost_enable;
 static unsigned int	sleep_usbdev_enable;
 static unsigned int	sleep_static_memctlr[4][3];
 
-/*
- * Define this to cause the value you write to /proc/sys/pm/sleep to
- * set the TOY timer for the amount of time you want to sleep.
- * This is done mainly for testing, but may be useful in other cases.
- * The value is number of 32KHz ticks to sleep.
- */
-#define SLEEP_TEST_TIMEOUT 1
-#ifdef	SLEEP_TEST_TIMEOUT
-static int sleep_ticks;
-static void wakeup_counter0_set(int ticks)
-{
-	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
-	au_sync();
-}
-#endif
-
 static void save_core_regs(void)
 {
 	extern void save_au1xxx_intctl(void);
@@ -190,287 +153,11 @@ static void restore_core_regs(void)
 	restore_au1xxx_intctl();
 }
 
-unsigned long suspend_mode;
-
-void wakeup_from_suspend(void)
-{
-	suspend_mode = 0;
-}
-
-int au_sleep(void)
+void au_sleep(void)
 {
-	unsigned long wakeup, flags;
-	extern void save_and_sleep(void);
-
-	spin_lock_irqsave(&pm_lock, flags);
-
 	save_core_regs();
-
 	flush_cache_all();
-
-	/**
-	 ** The code below is all system dependent and we should probably
-	 ** have a function call out of here to set this up.  You need
-	 ** to configure the GPIO or timer interrupts that will bring
-	 ** you out of sleep.
-	 ** For testing, the TOY counter wakeup is useful.
-	 **/
-#if 0
-	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
-
-	/* GPIO 6 can cause a wake up event */
-	wakeup = au_readl(SYS_WAKEMSK);
-	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
-	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
-#else
-	/* For testing, allow match20 to wake us up. */
-#ifdef SLEEP_TEST_TIMEOUT
-	wakeup_counter0_set(sleep_ticks);
-#endif
-	wakeup = 1 << 8;	/* turn on match20 wakeup   */
-	wakeup = 0;
-#endif
-	au_writel(1, SYS_WAKESRC);	/* clear cause */
-	au_sync();
-	au_writel(wakeup, SYS_WAKEMSK);
-	au_sync();
-
 	save_and_sleep();
-
-	/*
-	 * After a wakeup, the cpu vectors back to 0x1fc00000, so
-	 * it's up to the boot code to get us back here.
-	 */
 	restore_core_regs();
-	spin_unlock_irqrestore(&pm_lock, flags);
-	return 0;
-}
-
-static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
-		       void __user *buffer, size_t *len, loff_t *ppos)
-{
-#ifdef SLEEP_TEST_TIMEOUT
-#define TMPBUFLEN2 16
-	char buf[TMPBUFLEN2], *p;
-#endif
-
-	if (!write)
-		*len = 0;
-	else {
-#ifdef SLEEP_TEST_TIMEOUT
-		if (*len > TMPBUFLEN2 - 1)
-			return -EFAULT;
-		if (copy_from_user(buf, buffer, *len))
-			return -EFAULT;
-		buf[*len] = 0;
-		p = buf;
-		sleep_ticks = simple_strtoul(p, &p, 0);
-#endif
-
-		au_sleep();
-	}
-	return 0;
-}
-
-static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
-		      void __user *buffer, size_t *len, loff_t *ppos)
-{
-	int retval = 0, i;
-	unsigned long val, pll;
-#define TMPBUFLEN 64
-#define MAX_CPU_FREQ 396
-	char buf[TMPBUFLEN], *p;
-	unsigned long flags, intc0_mask, intc1_mask;
-	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
-	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
-	unsigned long baud_rate;
-
-	spin_lock_irqsave(&pm_lock, flags);
-	if (!write)
-		*len = 0;
-	else {
-		/* Parse the new frequency */
-		if (*len > TMPBUFLEN - 1) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		if (copy_from_user(buf, buffer, *len)) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		buf[*len] = 0;
-		p = buf;
-		val = simple_strtoul(p, &p, 0);
-		if (val > MAX_CPU_FREQ) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		pll = val / 12;
-		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
-			/* Revisit this for higher speed CPUs */
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		old_baud_base = get_au1x00_uart_baud_base();
-		old_cpu_freq = get_au1x00_speed();
-
-		new_cpu_freq = pll * 12 * 1000000;
-	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
-							    & 0x03) + 2) * 16));
-		set_au1x00_speed(new_cpu_freq);
-		set_au1x00_uart_baud_base(new_baud_base);
-
-		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
-		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
-			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
-
-		au_writel(pll, SYS_CPUPLL);
-		au_sync_delay(1);
-		au_writel(new_refresh, MEM_SDREFCFG);
-		au_sync_delay(1);
-
-		for (i = 0; i < 4; i++)
-			if (au_readl(UART_BASE + UART_MOD_CNTRL +
-				     i * 0x00100000) == 3) {
-				old_clk = au_readl(UART_BASE + UART_CLK +
-						   i * 0x00100000);
-				baud_rate = old_baud_base / old_clk;
-				/*
-				 * We won't get an exact baud rate and the error
-				 * could be significant enough that our new
-				 * calculation will result in a clock that will
-				 * give us a baud rate that's too far off from
-				 * what we really want.
-				 */
-				if (baud_rate > 100000)
-					baud_rate = 115200;
-				else if (baud_rate > 50000)
-					baud_rate = 57600;
-				else if (baud_rate > 30000)
-					baud_rate = 38400;
-				else if (baud_rate > 17000)
-					baud_rate = 19200;
-				else
-					baud_rate = 9600;
-				new_clk = new_baud_base / baud_rate;
-				au_writel(new_clk, UART_BASE + UART_CLK +
-					  i * 0x00100000);
-				au_sync_delay(10);
-			}
-	}
-
-	/*
-	 * We don't want _any_ interrupts other than match20. Otherwise our
-	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
-	 */
-	intc0_mask = save_local_and_disable(0);
-	intc1_mask = save_local_and_disable(1);
-	local_enable_irq(AU1000_TOY_MATCH2_INT);
-	spin_unlock_irqrestore(&pm_lock, flags);
-	au1000_calibrate_delay();
-	restore_local_and_enable(0, intc0_mask);
-	restore_local_and_enable(1, intc1_mask);
-
-	return retval;
-}
-
-
-static struct ctl_table pm_table[] = {
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "sleep",
-		.data		= NULL,
-		.maxlen		= 0,
-		.mode		= 0600,
-		.proc_handler	= &pm_do_sleep
-	},
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "freq",
-		.data		= NULL,
-		.maxlen		= 0,
-		.mode		= 0600,
-		.proc_handler	= &pm_do_freq
-	},
-	{}
-};
-
-static struct ctl_table pm_dir_table[] = {
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "pm",
-		.mode		= 0555,
-		.child		= pm_table
-	},
-	{}
-};
-
-/*
- * Initialize power interface
- */
-static int __init pm_init(void)
-{
-	/* init TOY to tick at 1Hz. No need to wait for access bits
-	 * since there's plenty of time between here and the first
-	 * suspend cycle.
-	 */
-	au_writel(32767, SYS_TOYTRIM);
-	au_writel(0, SYS_TOYWRITE);
-	au_sync();
-
-	register_sysctl_table(pm_dir_table);
-	return 0;
-}
-
-__initcall(pm_init);
-
-/*
- * This is right out of init/main.c
- */
-
-/*
- * This is the number of bits of precision for the loops_per_jiffy.
- * Each bit takes on average 1.5/HZ seconds.  This (like the original)
- * is a little better than 1%.
- */
-#define LPS_PREC 8
-
-static void au1000_calibrate_delay(void)
-{
-	unsigned long ticks, loopbit;
-	int lps_precision = LPS_PREC;
-
-	loops_per_jiffy = 1 << 12;
-
-	while (loops_per_jiffy <<= 1) {
-		/* Wait for "start of" clock tick */
-		ticks = jiffies;
-		while (ticks == jiffies)
-			/* nothing */ ;
-		/* Go ... */
-		ticks = jiffies;
-		__delay(loops_per_jiffy);
-		ticks = jiffies - ticks;
-		if (ticks)
-			break;
-	}
-
-	/*
-	 * Do a binary approximation to get loops_per_jiffy set to be equal
-	 * one clock (up to lps_precision bits)
-	 */
-	loops_per_jiffy >>= 1;
-	loopbit = loops_per_jiffy;
-	while (lps_precision-- && (loopbit >>= 1)) {
-		loops_per_jiffy |= loopbit;
-		ticks = jiffies;
-		while (ticks == jiffies);
-		ticks = jiffies;
-		__delay(loops_per_jiffy);
-		if (jiffies != ticks)	/* longer than 1 tick */
-			loops_per_jiffy &= ~loopbit;
-	}
 }
 #endif	/* CONFIG_PM */
-- 
1.5.6.3

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

* [PATCH v4 8/10] Alchemy: Fix PM code for Au1200/Au1550.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (6 preceding siblings ...)
  2008-07-29 17:03 ` [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts Manuel Lauss
@ 2008-07-29 17:04 ` Manuel Lauss
  2008-07-29 17:04 ` [PATCH v4 9/10] Alchemy: dbdma suspend/resume support Manuel Lauss
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:04 UTC (permalink / raw)
  To: linux-mips

Alchemy PM code doesn't compile on Au1200.
Copy the Au1200/Au1550 sleep code over from 2.4.

Tested on Au1200.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/platform.c |    5 +-
 arch/mips/au1000/common/power.c    |  111 +++++++++++++++++++++------------
 arch/mips/au1000/common/sleeper.S  |  121 ++++++++++++++++++++++--------------
 3 files changed, 150 insertions(+), 87 deletions(-)

diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
index 1e89560..fd8f27e 100644
--- a/arch/mips/au1000/common/platform.c
+++ b/arch/mips/au1000/common/platform.c
@@ -419,6 +419,7 @@ static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
 	return 0;
 }
 
+#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
 /*
  * This is the number of bits of precision for the loops_per_jiffy.
  * Each bit takes on average 1.5/HZ seconds.  This (like the original)
@@ -566,7 +567,7 @@ static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
 
 	return retval;
 }
-
+#endif
 
 static struct ctl_table pm_table[] = {
 	{
@@ -577,6 +578,7 @@ static struct ctl_table pm_table[] = {
 		.mode		= 0600,
 		.proc_handler	= &pm_do_sleep
 	},
+#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
 	{
 		.ctl_name	= CTL_UNNUMBERED,
 		.procname	= "freq",
@@ -585,6 +587,7 @@ static struct ctl_table pm_table[] = {
 		.mode		= 0600,
 		.proc_handler	= &pm_do_freq
 	},
+#endif
 	{}
 };
 
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index 4b0f6a1..ba4c946 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -26,13 +26,13 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/init.h>
-#include <asm/cacheflush.h>
-#include <asm/mach-au1x00/au1000.h>
-
 #ifdef CONFIG_PM
 
+#include <asm/mach-au1x00/au1000.h>
+
 extern void save_and_sleep(void);
+extern void save_au1xxx_intctl(void);
+extern void restore_au1xxx_intctl(void);
 
 /*
  * We need to save/restore a bunch of core registers that are
@@ -44,23 +44,18 @@ extern void save_and_sleep(void);
  * We only have to save/restore registers that aren't otherwise
  * done as part of a driver pm_* function.
  */
-static unsigned int	sleep_aux_pll_cntrl;
-static unsigned int	sleep_cpu_pll_cntrl;
-static unsigned int	sleep_pin_function;
-static unsigned int	sleep_uart0_inten;
-static unsigned int	sleep_uart0_fifoctl;
-static unsigned int	sleep_uart0_linectl;
-static unsigned int	sleep_uart0_clkdiv;
-static unsigned int	sleep_uart0_enable;
-static unsigned int	sleep_usbhost_enable;
-static unsigned int	sleep_usbdev_enable;
-static unsigned int	sleep_static_memctlr[4][3];
+static unsigned int sleep_uart0_inten;
+static unsigned int sleep_uart0_fifoctl;
+static unsigned int sleep_uart0_linectl;
+static unsigned int sleep_uart0_clkdiv;
+static unsigned int sleep_uart0_enable;
+static unsigned int sleep_usb[2];
+static unsigned int sleep_sys_clocks[5];
+static unsigned int sleep_sys_pinfunc;
+static unsigned int sleep_static_memctlr[4][3];
 
 static void save_core_regs(void)
 {
-	extern void save_au1xxx_intctl(void);
-	extern void pm_eth0_shutdown(void);
-
 	/*
 	 * Do the serial ports.....these really should be a pm_*
 	 * registered function by the driver......but of course the
@@ -72,31 +67,45 @@ static void save_core_regs(void)
 	sleep_uart0_linectl = au_readl(UART0_ADDR + UART_LCR);
 	sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK);
 	sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL);
+	au_sync();
 
+#ifndef CONFIG_SOC_AU1200
 	/* Shutdown USB host/device. */
-	sleep_usbhost_enable = au_readl(USB_HOST_CONFIG);
+	sleep_usb[0] = au_readl(USB_HOST_CONFIG);
 
 	/* There appears to be some undocumented reset register.... */
-	au_writel(0, 0xb0100004); au_sync();
-	au_writel(0, USB_HOST_CONFIG); au_sync();
+	au_writel(0, 0xb0100004);
+	au_sync();
+	au_writel(0, USB_HOST_CONFIG);
+	au_sync();
+
+	sleep_usb[1] = au_readl(USBD_ENABLE);
+	au_writel(0, USBD_ENABLE);
+	au_sync();
 
-	sleep_usbdev_enable = au_readl(USBD_ENABLE);
-	au_writel(0, USBD_ENABLE); au_sync();
+#else	/* AU1200 */
+
+	/* enable access to OTG mmio so we can save OTG CAP/MUX.
+	 * FIXME: write an OTG driver and move this stuff there!
+	 */
+	au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4);
+	au_sync();
+	sleep_usb[0] = au_readl(0xb4020020);	/* OTG_CAP */
+	sleep_usb[1] = au_readl(0xb4020024);	/* OTG_MUX */
+#endif
 
 	/* Save interrupt controller state. */
 	save_au1xxx_intctl();
 
 	/* Clocks and PLLs. */
-	sleep_aux_pll_cntrl = au_readl(SYS_AUXPLL);
-
-	/*
-	 * We don't really need to do this one, but unless we
-	 * write it again it won't have a valid value if we
-	 * happen to read it.
-	 */
-	sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL);
+	sleep_sys_clocks[0] = au_readl(SYS_FREQCTRL0);
+	sleep_sys_clocks[1] = au_readl(SYS_FREQCTRL1);
+	sleep_sys_clocks[2] = au_readl(SYS_CLKSRC);
+	sleep_sys_clocks[3] = au_readl(SYS_CPUPLL);
+	sleep_sys_clocks[4] = au_readl(SYS_AUXPLL);
 
-	sleep_pin_function = au_readl(SYS_PINFUNC);
+	/* pin mux config */
+	sleep_sys_pinfunc = au_readl(SYS_PINFUNC);
 
 	/* Save the static memory controller configuration. */
 	sleep_static_memctlr[0][0] = au_readl(MEM_STCFG0);
@@ -115,12 +124,37 @@ static void save_core_regs(void)
 
 static void restore_core_regs(void)
 {
-	extern void restore_au1xxx_intctl(void);
-	extern void wakeup_counter0_adjust(void);
-
-	au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync();
-	au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync();
-	au_writel(sleep_pin_function, SYS_PINFUNC); au_sync();
+	/* restore clock configuration.  Writing CPUPLL last will
+	 * stall a bit and stabilize other clocks (unless this is
+	 * one of those Au1000 with a write-only PLL, where we dont
+	 * have a valid value)
+	 */
+	au_writel(sleep_sys_clocks[0], SYS_FREQCTRL0);
+	au_writel(sleep_sys_clocks[1], SYS_FREQCTRL1);
+	au_writel(sleep_sys_clocks[2], SYS_CLKSRC);
+	au_writel(sleep_sys_clocks[4], SYS_AUXPLL);
+	if (!au1xxx_cpu_has_pll_wo())
+		au_writel(sleep_sys_clocks[3], SYS_CPUPLL);
+	au_sync();
+
+	au_writel(sleep_sys_pinfunc, SYS_PINFUNC);
+	au_sync();
+
+#ifndef CONFIG_SOC_AU1200
+	au_writel(sleep_usb[0], USB_HOST_CONFIG);
+	au_writel(sleep_usb[1], USBD_ENABLE);
+	au_sync();
+#else
+	/* enable accces to OTG memory */
+	au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4);
+	au_sync();
+
+	/* restore OTG caps and port mux. */
+	au_writel(sleep_usb[0], 0xb4020020 + 0);	/* OTG_CAP */
+	au_sync();
+	au_writel(sleep_usb[1], 0xb4020020 + 4);	/* OTG_MUX */
+	au_sync();
+#endif
 
 	/* Restore the static memory controller configuration. */
 	au_writel(sleep_static_memctlr[0][0], MEM_STCFG0);
@@ -156,7 +190,6 @@ static void restore_core_regs(void)
 void au_sleep(void)
 {
 	save_core_regs();
-	flush_cache_all();
 	save_and_sleep();
 	restore_core_regs();
 }
diff --git a/arch/mips/au1000/common/sleeper.S b/arch/mips/au1000/common/sleeper.S
index 4b3cf02..76506aa 100644
--- a/arch/mips/au1000/common/sleeper.S
+++ b/arch/mips/au1000/common/sleeper.S
@@ -15,9 +15,11 @@
 #include <asm/regdef.h>
 #include <asm/stackframe.h>
 
+	.extern __flush_cache_all
+
 	.text
-	.set	macro
-	.set	noat
+	.set noreorder
+	.set noat
 	.align	5
 
 /* Save all of the processor general registers and go to sleep.
@@ -33,14 +35,6 @@ LEAF(save_and_sleep)
 	sw	$5, PT_R5(sp)
 	sw	$6, PT_R6(sp)
 	sw	$7, PT_R7(sp)
-	sw	$8, PT_R8(sp)
-	sw	$9, PT_R9(sp)
-	sw	$10, PT_R10(sp)
-	sw	$11, PT_R11(sp)
-	sw	$12, PT_R12(sp)
-	sw	$13, PT_R13(sp)
-	sw	$14, PT_R14(sp)
-	sw	$15, PT_R15(sp)
 	sw	$16, PT_R16(sp)
 	sw	$17, PT_R17(sp)
 	sw	$18, PT_R18(sp)
@@ -49,12 +43,9 @@ LEAF(save_and_sleep)
 	sw	$21, PT_R21(sp)
 	sw	$22, PT_R22(sp)
 	sw	$23, PT_R23(sp)
-	sw	$24, PT_R24(sp)
-	sw	$25, PT_R25(sp)
 	sw	$26, PT_R26(sp)
 	sw	$27, PT_R27(sp)
 	sw	$28, PT_R28(sp)
-	sw	$29, PT_R29(sp)
 	sw	$30, PT_R30(sp)
 	sw	$31, PT_R31(sp)
 	mfc0	k0, CP0_STATUS
@@ -66,44 +57,89 @@ LEAF(save_and_sleep)
 	mfc0	k0, CP0_CONFIG
 	sw	k0, 0x14(sp)
 
+	/* flush caches to make sure context is in memory */
+	la	t1, __flush_cache_all
+	lw	t0, 0(t1)
+	jal	t0
+	 nop
+
 	/* Now set up the scratch registers so the boot rom will
 	 * return to this point upon wakeup.
+	 * sys_scratch0 : SP
+	 * sys_scratch1 : RA
 	 */
-	la	k0, 1f
-	lui	k1, 0xb190
-	ori	k1, 0x18
-	sw	sp, 0(k1)
-	ori 	k1, 0x1c
-	sw	k0, 0(k1)
-
-/* Put SDRAM into self refresh.  Preload instructions into cache,
- * issue a precharge, then auto refresh, then sleep commands to it.
- */
- 	la	t0, sdsleep
+	li	t3, 0xb1900000		/* sys_xxx */
+	sw	sp, 0x0018(t3)
+	la	k0, resume_from_sleep
+	sw	k0, 0x001c(t3)
+
+	/* Put SDRAM into self refresh.  Preload instructions into cache,
+	 * issue a precharge, then auto refresh, then sleep commands to it.
+	 */
+	la	t0, sdsleep
 	.set	mips3
- 	cache	0x14, 0(t0)
- 	cache	0x14, 32(t0)
- 	cache	0x14, 64(t0)
- 	cache	0x14, 96(t0)
+	cache	0x14, 0(t0)
+	cache	0x14, 32(t0)
+	cache	0x14, 64(t0)
+	cache	0x14, 96(t0)
 	.set	mips0
 
 sdsleep:
-	lui 	k0, 0xb400
-	sw	zero, 0x001c(k0)	/* Precharge */
-	sw	zero, 0x0020(k0)	/* Auto refresh */
-	sw	zero, 0x0030(k0)	/* SDRAM sleep */
+	li 	a0, 0xb4000000		/* mem_xxx */
+#if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1100) ||	\
+    defined(CONFIG_SOC_AU1500)
+	sw	zero, 0x001c(a0) 	/* Precharge */
+	sync
+	sw	zero, 0x0020(a0)	/* Auto Refresh */
+	sync
+	sw	zero, 0x0030(a0)  	/* Sleep */
 	sync
+#endif
 
-	lui 	k1, 0xb190
-	sw	zero, 0x0078(k1)	/* get ready  to sleep */
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+	sw	zero, 0x08c0(a0) 	/* Precharge */
 	sync
-	sw	zero, 0x007c(k1)	/* Put processor to sleep */
+	sw	zero, 0x08d0(a0)	/* Self Refresh */
 	sync
 
+	/* wait for sdram to enter self-refresh mode */
+	li 	t0, 0x01000000
+sref_wait_loop:
+	lw 	t1, 0x0850(a0)		/* mem_sdstat */
+	and	t2, t1, t0
+	beq	t2, zero, sref_wait_loop
+	 nop
+
+	/* Disable SDRAM clocks */
+	li	t0, ~0x30000000
+	lw 	t1, 0x0840(a0)		/* mem_sdconfiga */
+	and 	t1, t0, t1		/* clear CE[1:0] */
+	sw 	t1, 0x0840(a0)		/* mem_sdconfiga */
+	sync
+#endif
+
+	/* put power supply and processor to sleep */
+	sw	zero, 0x0078(t3)	/* sys_slppwr */
+	sync
+	sw	zero, 0x007c(t3)	/* sys_sleep */
+	sync
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+
 	/* This is where we return upon wakeup.
 	 * Reload all of the registers and return.
 	 */
-1:	nop
+resume_from_sleep:
+	nop
+	.set nomacro
+	.set noat
+
 	lw	k0, 0x20(sp)
 	mtc0	k0, CP0_STATUS
 	lw	k0, 0x1c(sp)
@@ -117,6 +153,7 @@ sdsleep:
 	 * the write-only Config[OD] bit and set it back to one...
 	 */
 	jal	au1x00_fixup_config_od
+	 nop
 	lw	$1, PT_R1(sp)
 	lw	$2, PT_R2(sp)
 	lw	$3, PT_R3(sp)
@@ -124,14 +161,6 @@ sdsleep:
 	lw	$5, PT_R5(sp)
 	lw	$6, PT_R6(sp)
 	lw	$7, PT_R7(sp)
-	lw	$8, PT_R8(sp)
-	lw	$9, PT_R9(sp)
-	lw	$10, PT_R10(sp)
-	lw	$11, PT_R11(sp)
-	lw	$12, PT_R12(sp)
-	lw	$13, PT_R13(sp)
-	lw	$14, PT_R14(sp)
-	lw	$15, PT_R15(sp)
 	lw	$16, PT_R16(sp)
 	lw	$17, PT_R17(sp)
 	lw	$18, PT_R18(sp)
@@ -140,15 +169,13 @@ sdsleep:
 	lw	$21, PT_R21(sp)
 	lw	$22, PT_R22(sp)
 	lw	$23, PT_R23(sp)
-	lw	$24, PT_R24(sp)
-	lw	$25, PT_R25(sp)
 	lw	$26, PT_R26(sp)
 	lw	$27, PT_R27(sp)
 	lw	$28, PT_R28(sp)
-	lw	$29, PT_R29(sp)
 	lw	$30, PT_R30(sp)
 	lw	$31, PT_R31(sp)
 	addiu	sp, PT_SIZE
 
 	jr	ra
+	 nop
 END(save_and_sleep)
-- 
1.5.6.3

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

* [PATCH v4 9/10] Alchemy: dbdma suspend/resume support.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (7 preceding siblings ...)
  2008-07-29 17:04 ` [PATCH v4 8/10] Alchemy: Fix PM code for Au1200/Au1550 Manuel Lauss
@ 2008-07-29 17:04 ` Manuel Lauss
  2008-07-29 17:05 ` [PATCH v4 10/10] Alchemy: new demoboard userspace suspend interface Manuel Lauss
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:04 UTC (permalink / raw)
  To: linux-mips

Implement suspend/resume for DBDMA controller and its channels.

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/dbdma.c |   65 +++++++++++++++++++++++++++++++++++++++
 arch/mips/au1000/common/power.c |   12 +++++++
 2 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/arch/mips/au1000/common/dbdma.c b/arch/mips/au1000/common/dbdma.c
index 601ee91..3ab6d80 100644
--- a/arch/mips/au1000/common/dbdma.c
+++ b/arch/mips/au1000/common/dbdma.c
@@ -174,6 +174,11 @@ static dbdev_tab_t dbdev_tab[] = {
 
 #define DBDEV_TAB_SIZE	ARRAY_SIZE(dbdev_tab)
 
+#ifdef CONFIG_PM
+static u32 au1xxx_dbdma_pm_regs[NUM_DBDMA_CHANS + 1][8];
+#endif
+
+
 static chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS];
 
 static dbdev_tab_t *find_dbdev_id(u32 id)
@@ -975,4 +980,64 @@ u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr)
 	return nbytes;
 }
 
+#ifdef CONFIG_PM
+void au1xxx_dbdma_suspend(void)
+{
+	int i;
+	u32 addr;
+
+	addr = DDMA_GLOBAL_BASE;
+	au1xxx_dbdma_pm_regs[0][0] = au_readl(addr + 0x00);
+	au1xxx_dbdma_pm_regs[0][1] = au_readl(addr + 0x04);
+	au1xxx_dbdma_pm_regs[0][2] = au_readl(addr + 0x08);
+	au1xxx_dbdma_pm_regs[0][3] = au_readl(addr + 0x0c);
+
+	/* save channel configurations */
+	for (i = 1, addr = DDMA_CHANNEL_BASE; i < NUM_DBDMA_CHANS; i++) {
+		au1xxx_dbdma_pm_regs[i][0] = au_readl(addr + 0x00);
+		au1xxx_dbdma_pm_regs[i][1] = au_readl(addr + 0x04);
+		au1xxx_dbdma_pm_regs[i][2] = au_readl(addr + 0x08);
+		au1xxx_dbdma_pm_regs[i][3] = au_readl(addr + 0x0c);
+		au1xxx_dbdma_pm_regs[i][4] = au_readl(addr + 0x10);
+		au1xxx_dbdma_pm_regs[i][5] = au_readl(addr + 0x14);
+		au1xxx_dbdma_pm_regs[i][6] = au_readl(addr + 0x18);
+
+		/* halt channel */
+		au_writel(au1xxx_dbdma_pm_regs[i][0] & ~1, addr + 0x00);
+		au_sync();
+		while (!(au_readl(addr + 0x14) & 1))
+			au_sync();
+
+		addr += 0x100;	/* next channel base */
+	}
+	/* disable channel interrupts */
+	au_writel(0, DDMA_GLOBAL_BASE + 0x0c);
+	au_sync();
+}
+
+void au1xxx_dbdma_resume(void)
+{
+	int i;
+	u32 addr;
+
+	addr = DDMA_GLOBAL_BASE;
+	au_writel(au1xxx_dbdma_pm_regs[0][0], addr + 0x00);
+	au_writel(au1xxx_dbdma_pm_regs[0][1], addr + 0x04);
+	au_writel(au1xxx_dbdma_pm_regs[0][2], addr + 0x08);
+	au_writel(au1xxx_dbdma_pm_regs[0][3], addr + 0x0c);
+
+	/* restore channel configurations */
+	for (i = 1, addr = DDMA_CHANNEL_BASE; i < NUM_DBDMA_CHANS; i++) {
+		au_writel(au1xxx_dbdma_pm_regs[i][0], addr + 0x00);
+		au_writel(au1xxx_dbdma_pm_regs[i][1], addr + 0x04);
+		au_writel(au1xxx_dbdma_pm_regs[i][2], addr + 0x08);
+		au_writel(au1xxx_dbdma_pm_regs[i][3], addr + 0x0c);
+		au_writel(au1xxx_dbdma_pm_regs[i][4], addr + 0x10);
+		au_writel(au1xxx_dbdma_pm_regs[i][5], addr + 0x14);
+		au_writel(au1xxx_dbdma_pm_regs[i][6], addr + 0x18);
+		au_sync();
+		addr += 0x100;	/* next channel base */
+	}
+}
+#endif	/* CONFIG_PM */
 #endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index ba4c946..1057a73 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -33,6 +33,10 @@
 extern void save_and_sleep(void);
 extern void save_au1xxx_intctl(void);
 extern void restore_au1xxx_intctl(void);
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+extern void au1xxx_dbdma_suspend(void);
+extern void au1xxx_dbdma_resume(void);
+#endif
 
 /*
  * We need to save/restore a bunch of core registers that are
@@ -120,6 +124,10 @@ static void save_core_regs(void)
 	sleep_static_memctlr[3][0] = au_readl(MEM_STCFG3);
 	sleep_static_memctlr[3][1] = au_readl(MEM_STTIME3);
 	sleep_static_memctlr[3][2] = au_readl(MEM_STADDR3);
+
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+	au1xxx_dbdma_suspend();
+#endif
 }
 
 static void restore_core_regs(void)
@@ -185,6 +193,10 @@ static void restore_core_regs(void)
 	}
 
 	restore_au1xxx_intctl();
+
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+	au1xxx_dbdma_resume();
+#endif
 }
 
 void au_sleep(void)
-- 
1.5.6.3

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

* [PATCH v4 10/10] Alchemy: new demoboard userspace suspend interface.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (8 preceding siblings ...)
  2008-07-29 17:04 ` [PATCH v4 9/10] Alchemy: dbdma suspend/resume support Manuel Lauss
@ 2008-07-29 17:05 ` Manuel Lauss
  2008-07-29 21:33 ` [PATCH v4 0/10] Alchemy updates Kevin Hickey
  2008-08-06 20:39 ` Kevin Hickey
  11 siblings, 0 replies; 18+ messages in thread
From: Manuel Lauss @ 2008-07-29 17:05 UTC (permalink / raw)
  To: linux-mips

Replace the current sysctl-based suspend interface with a new sysfs-
based one which also uses the Linux-2.6 suspend model.

To configure wakeup sources, a subtree for the demoboards is created
under /sys/power/db1x:

sys/
`-- power
    `-- db1x
        |-- gpio0
        |-- gpio1
        |-- gpio2
        |-- gpio3
        |-- gpio4
        |-- gpio5
        |-- gpio6
        |-- gpio7
        |-- timer
        |-- timer_timeout
        |-- uptime
        |-- wakemsk
        `-- wakesrc

The nodes 'gpio[0-7]' and 'timer' configure the GPIO0..7 and M2
bits of the SYS_WAKEMSK (wakeup source enable) register.  Writing '1'
enables a wakesource, 0 disables it.

The 'timer_timeout' node holds the timeout in seconds after which the
TOYMATCH2 event should wake the system.

The 'wakesrc' node holds the SYS_WAKESRC register after wakeup (in hex),
the 'wakemsk' node can be used to get/set the wakeup mask directly.

'uptime' gives access to the TOY counter value; it can be used for example
to track total time in seconds since last kernel boot.

For example, to have the timer wake the system after 10 seconds of sleep,
the following must be done in userspace:

echo 10 > /sys/power/db1x/timer_timeout
echo 1 > /sys/power/db1x/timer
echo mem > /sys/power/sleep

This patch also removes the homebrew CPU frequency switching code.  I don't
understand how it could have ever worked reliably; it does not communicate
the clock changes to other devices tied to BCLK and does not adjust memory
controller timings (which is not a problem when the CPU is never clocked
higher than what was set by the bootloader).

Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
---
 arch/mips/au1000/common/irq.c         |   57 +----
 arch/mips/au1000/common/platform.c    |  423 ++++++++++++++-------------------
 include/asm-mips/mach-au1x00/au1000.h |    4 +
 3 files changed, 193 insertions(+), 291 deletions(-)

diff --git a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c
index 40c6cec..f5489c3 100644
--- a/arch/mips/au1000/common/irq.c
+++ b/arch/mips/au1000/common/irq.c
@@ -44,8 +44,6 @@
 
 void (*board_init_irq)(void) __initdata = NULL;
 
-static DEFINE_SPINLOCK(irq_lock);
-
 #ifdef CONFIG_PM
 
 /*
@@ -130,7 +128,7 @@ void restore_au1xxx_intctl(void)
 #endif /* CONFIG_PM */
 
 
-inline void local_enable_irq(unsigned int irq_nr)
+static void local_enable_irq(unsigned int irq_nr)
 {
 	unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
 
@@ -145,7 +143,7 @@ inline void local_enable_irq(unsigned int irq_nr)
 }
 
 
-inline void local_disable_irq(unsigned int irq_nr)
+static void local_disable_irq(unsigned int irq_nr)
 {
 	unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
 
@@ -160,7 +158,7 @@ inline void local_disable_irq(unsigned int irq_nr)
 }
 
 
-static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr)
+static void mask_and_ack_rise_edge_irq(unsigned int irq_nr)
 {
 	unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
 
@@ -175,7 +173,7 @@ static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr)
 }
 
 
-static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr)
+static void mask_and_ack_fall_edge_irq(unsigned int irq_nr)
 {
 	unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
 
@@ -190,7 +188,7 @@ static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr)
 }
 
 
-static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr)
+static void mask_and_ack_either_edge_irq(unsigned int irq_nr)
 {
 	unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE;
 
@@ -210,7 +208,7 @@ static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr)
 	au_sync();
 }
 
-static inline void mask_and_ack_level_irq(unsigned int irq_nr)
+static void mask_and_ack_level_irq(unsigned int irq_nr)
 {
 	local_disable_irq(irq_nr);
 	au_sync();
@@ -235,49 +233,6 @@ static void end_irq(unsigned int irq_nr)
 #endif
 }
 
-unsigned long save_local_and_disable(int controller)
-{
-	int i;
-	unsigned long flags, mask;
-
-	spin_lock_irqsave(&irq_lock, flags);
-	if (controller) {
-		mask = au_readl(IC1_MASKSET);
-		for (i = 32; i < 64; i++)
-			local_disable_irq(i);
-	} else {
-		mask = au_readl(IC0_MASKSET);
-		for (i = 0; i < 32; i++)
-			local_disable_irq(i);
-	}
-	spin_unlock_irqrestore(&irq_lock, flags);
-
-	return mask;
-}
-
-void restore_local_and_enable(int controller, unsigned long mask)
-{
-	int i;
-	unsigned long flags, new_mask;
-
-	spin_lock_irqsave(&irq_lock, flags);
-	for (i = 0; i < 32; i++)
-		if (mask & (1 << i)) {
-			if (controller)
-				local_enable_irq(i + 32);
-			else
-				local_enable_irq(i);
-		}
-
-	if (controller)
-		new_mask = au_readl(IC1_MASKSET);
-	else
-		new_mask = au_readl(IC0_MASKSET);
-
-	spin_unlock_irqrestore(&irq_lock, flags);
-}
-
-
 static struct irq_chip rise_edge_irq_type = {
 	.name		= "Au1000 Rise Edge",
 	.ack		= mask_and_ack_rise_edge_irq,
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
index fd8f27e..01aa504 100644
--- a/arch/mips/au1000/common/platform.c
+++ b/arch/mips/au1000/common/platform.c
@@ -23,10 +23,6 @@
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 #include <linux/interrupt.h>
-#include <linux/pm.h>
-#include <linux/spinlock.h>
-#include <linux/sysctl.h>
-#include <linux/uaccess.h>
 
 #include <asm/mach-au1x00/au1xxx.h>
 
@@ -336,269 +332,214 @@ arch_initcall(au1xxx_platform_init);
 
 #ifdef CONFIG_PM
 
-static DEFINE_SPINLOCK(pm_lock);
+/*
+ * Generic suspend for db1x.
+ * This codes exports a few sysfs nodes under /sys/power/db1x/
+ * which can be used by userspace to en/disable certain wakeup sources
+ * and configure the timeout after which the the TOYMATCH2 irq is to
+ * trigger a wakeup.
+ */
+
+#include <linux/kobject.h>
+#include <linux/suspend.h>
+#include <linux/sysfs.h>
 
-extern unsigned long save_local_and_disable(int controller);
-extern void restore_local_and_enable(int controller, unsigned long mask);
-extern void local_enable_irq(unsigned int irq_nr);
 extern void au_sleep(void);
 
-/*
- * Define this to cause the value you write to /proc/sys/pm/sleep to
- * set the TOY timer for the amount of time you want to sleep.
- * This is done mainly for testing, but may be useful in other cases.
- * The value is number of 32KHz ticks to sleep.
- */
-#define SLEEP_TEST_TIMEOUT 1
-#ifdef	SLEEP_TEST_TIMEOUT
-static int sleep_ticks;
-static void wakeup_counter0_set(int ticks)
-{
-	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
-	au_sync();
-}
-#endif
+static unsigned long db1x_pm_sleep_secs;
+static unsigned long db1x_pm_wakemsk;
+static unsigned long db1x_pm_last_wakesrc;
 
-static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
-		       void __user *buffer, size_t *len, loff_t *ppos)
+static int db1x_pm_enter(suspend_state_t state)
 {
-	unsigned long wakeup, flags;
-#ifdef SLEEP_TEST_TIMEOUT
-#define TMPBUFLEN2 16
-	char buf[TMPBUFLEN2], *p;
-#endif
+	/* enable GPIO based wakeup */
+	au_writel(1, SYS_PININPUTEN);
+	au_sync();
 
-	spin_lock_irqsave(&pm_lock, flags);
+	/* clear and setup wake cause and source */
+	au_writel(0, SYS_WAKEMSK);
+	au_sync();
+	au_writel(0, SYS_WAKESRC);
+	au_sync();
 
-	if (!write) {
-		*len = 0;
-		spin_unlock_irqrestore(&pm_lock, flags);
-		return 0;
-	}
+	au_writel(db1x_pm_wakemsk, SYS_WAKEMSK);
+	au_sync();
 
-#ifdef SLEEP_TEST_TIMEOUT
-	if (*len > TMPBUFLEN2 - 1)
-		return -EFAULT;
-	if (copy_from_user(buf, buffer, *len))
-		return -EFAULT;
-	buf[*len] = 0;
-	p = buf;
-	sleep_ticks = simple_strtoul(p, &p, 0);
-#endif
+	/* setup 1Hz-timer-based wakeup */
+	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
+		asm volatile ("nop");
 
-	/**
-	 ** The code below is all system dependent and we should probably
-	 ** have a function call out of here to set this up.  You need
-	 ** to configure the GPIO or timer interrupts that will bring
-	 ** you out of sleep.
-	 ** For testing, the TOY counter wakeup is useful.
-	 **/
-#if 0
-	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
-
-	/* GPIO 6 can cause a wake up event */
-	wakeup = au_readl(SYS_WAKEMSK);
-	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
-	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
-#else
-	/* For testing, allow match20 to wake us up. */
-#ifdef SLEEP_TEST_TIMEOUT
-	wakeup_counter0_set(sleep_ticks);
-#endif
-	wakeup = 1 << 8;	/* turn on match20 wakeup   */
-#endif
-	au_writel(1, SYS_WAKESRC);	/* clear cause */
-	au_sync();
-	au_writel(wakeup, SYS_WAKEMSK);
+	au_writel(au_readl(SYS_TOYREAD) + db1x_pm_sleep_secs, SYS_TOYMATCH2);
 	au_sync();
 
-	au_sleep();
+	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
+		asm volatile ("nop");
 
-	spin_unlock_irqrestore(&pm_lock, flags);
+	/* ...and now the sandman can come! */
+	au_sleep();
 
 	return 0;
 }
 
-#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
-/*
- * This is the number of bits of precision for the loops_per_jiffy.
- * Each bit takes on average 1.5/HZ seconds.  This (like the original)
- * is a little better than 1%.
- */
-#define LPS_PREC 8
-
-static void au1000_calibrate_delay(void)
+static int db1x_pm_begin(suspend_state_t state)
 {
-	unsigned long ticks, loopbit;
-	int lps_precision = LPS_PREC;
-
-	loops_per_jiffy = 1 << 12;
-
-	while (loops_per_jiffy <<= 1) {
-		/* Wait for "start of" clock tick */
-		ticks = jiffies;
-		while (ticks == jiffies)
-			/* nothing */ ;
-		/* Go ... */
-		ticks = jiffies;
-		__delay(loops_per_jiffy);
-		ticks = jiffies - ticks;
-		if (ticks)
-			break;
+	if (!db1x_pm_wakemsk) {
+		printk(KERN_ERR "db1x: no wakeup source activated!\n");
+		return -EINVAL;
 	}
 
-	/*
-	 * Do a binary approximation to get loops_per_jiffy set to be equal
-	 * one clock (up to lps_precision bits)
+	return 0;
+}
+
+static void db1x_pm_end(void)
+{
+	/* read and store wakeup source, the clear the register. To
+	 * be able to clear it, WAKEMSK must be cleared first.
 	 */
-	loops_per_jiffy >>= 1;
-	loopbit = loops_per_jiffy;
-	while (lps_precision-- && (loopbit >>= 1)) {
-		loops_per_jiffy |= loopbit;
-		ticks = jiffies;
-		while (ticks == jiffies);
-		ticks = jiffies;
-		__delay(loops_per_jiffy);
-		if (jiffies != ticks)	/* longer than 1 tick */
-			loops_per_jiffy &= ~loopbit;
-	}
+	db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
+
+	au_writel(0, SYS_WAKEMSK);
+	au_writel(0, SYS_WAKESRC);
+	au_sync();
+
 }
 
-static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
-		      void __user *buffer, size_t *len, loff_t *ppos)
+static struct platform_suspend_ops db1x_pm_ops = {
+	.valid		= suspend_valid_only_mem,
+	.begin		= db1x_pm_begin,
+	.enter		= db1x_pm_enter,
+	.end		= db1x_pm_end,
+};
+
+#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
+
+static ssize_t db1x_pmattr_show(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				char *buf)
 {
-	int retval = 0, i;
-	unsigned long val, pll;
-#define TMPBUFLEN 64
-#define MAX_CPU_FREQ 396
-	char buf[TMPBUFLEN], *p;
-	unsigned long flags, intc0_mask, intc1_mask;
-	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
-	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
-	unsigned long baud_rate;
-
-	spin_lock_irqsave(&pm_lock, flags);
-	if (!write)
-		*len = 0;
-	else {
-		/* Parse the new frequency */
-		if (*len > TMPBUFLEN - 1) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		if (copy_from_user(buf, buffer, *len)) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		buf[*len] = 0;
-		p = buf;
-		val = simple_strtoul(p, &p, 0);
-		if (val > MAX_CPU_FREQ) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		pll = val / 12;
-		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
-			/* Revisit this for higher speed CPUs */
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		old_baud_base = get_au1x00_uart_baud_base();
-		old_cpu_freq = get_au1x00_speed();
-
-		new_cpu_freq = pll * 12 * 1000000;
-	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
-							    & 0x03) + 2) * 16));
-		set_au1x00_speed(new_cpu_freq);
-		set_au1x00_uart_baud_base(new_baud_base);
-
-		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
-		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
-			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
-
-		au_writel(pll, SYS_CPUPLL);
-		au_sync_delay(1);
-		au_writel(new_refresh, MEM_SDREFCFG);
-		au_sync_delay(1);
-
-		for (i = 0; i < 4; i++)
-			if (au_readl(UART_BASE + UART_MOD_CNTRL +
-				     i * 0x00100000) == 3) {
-				old_clk = au_readl(UART_BASE + UART_CLK +
-						   i * 0x00100000);
-				baud_rate = old_baud_base / old_clk;
-				/*
-				 * We won't get an exact baud rate and the error
-				 * could be significant enough that our new
-				 * calculation will result in a clock that will
-				 * give us a baud rate that's too far off from
-				 * what we really want.
-				 */
-				if (baud_rate > 100000)
-					baud_rate = 115200;
-				else if (baud_rate > 50000)
-					baud_rate = 57600;
-				else if (baud_rate > 30000)
-					baud_rate = 38400;
-				else if (baud_rate > 17000)
-					baud_rate = 19200;
-				else
-					baud_rate = 9600;
-				new_clk = new_baud_base / baud_rate;
-				au_writel(new_clk, UART_BASE + UART_CLK +
-					  i * 0x00100000);
-				au_sync_delay(10);
-			}
+	int idx;
+
+	if (ATTRCMP(timer_timeout))
+		return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
+
+	else if (ATTRCMP(timer))
+		return sprintf(buf, "%u\n",
+				!!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
+
+	else if (ATTRCMP(wakesrc))
+		return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
+
+	else if (ATTRCMP(uptime))
+		return sprintf(buf, "%lu\n",
+				(unsigned long)au_readl(SYS_TOYREAD));
+
+	else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
+		 ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
+		 ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
+		idx = (attr->attr.name)[4] - '0';
+		return sprintf(buf, "%d\n",
+			!!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
+
+	} else if (ATTRCMP(wakemsk)) {
+		return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
 	}
 
-	/*
-	 * We don't want _any_ interrupts other than match20. Otherwise our
-	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
-	 */
-	intc0_mask = save_local_and_disable(0);
-	intc1_mask = save_local_and_disable(1);
-	local_enable_irq(AU1000_TOY_MATCH2_INT);
-	spin_unlock_irqrestore(&pm_lock, flags);
-	au1000_calibrate_delay();
-	restore_local_and_enable(0, intc0_mask);
-	restore_local_and_enable(1, intc1_mask);
-
-	return retval;
+	return -ENOENT;
 }
-#endif
 
-static struct ctl_table pm_table[] = {
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "sleep",
-		.data		= NULL,
-		.maxlen		= 0,
-		.mode		= 0600,
-		.proc_handler	= &pm_do_sleep
-	},
-#if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550)
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "freq",
-		.data		= NULL,
-		.maxlen		= 0,
-		.mode		= 0600,
-		.proc_handler	= &pm_do_freq
-	},
-#endif
-	{}
+static ssize_t db1x_pmattr_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *instr,
+				 size_t bytes)
+{
+	unsigned long l;
+	int tmp;
+
+	if (ATTRCMP(timer_timeout)) {
+		tmp = strict_strtoul(instr, 0, &l);
+		if (tmp)
+			return tmp;
+
+		db1x_pm_sleep_secs = l;
+
+	} else if (ATTRCMP(timer)) {
+		if (instr[0] != '0')
+			db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
+		else
+			db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
+
+	} else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
+		   ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
+		   ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
+		tmp = (attr->attr.name)[4] - '0';
+		if (instr[0] != '0')
+			db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
+		else
+			db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
+
+	} else if (ATTRCMP(uptime)) {
+		tmp = strict_strtoul(instr, 0, &l);
+		if (tmp)
+			return tmp;
+
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S)
+			asm volatile ("nop");
+		au_writel(l, SYS_TOYWRITE);
+		au_sync();
+
+	} else if (ATTRCMP(wakemsk)) {
+		tmp = strict_strtoul(instr, 0, &l);
+		if (tmp)
+			return tmp;
+
+		db1x_pm_wakemsk = l & 0x0000003f;
+
+	} else
+		bytes = -ENOENT;
+
+	return bytes;
+}
+
+#define ATTR(x)							\
+	static struct kobj_attribute x##_attribute = 		\
+		__ATTR(x, 0664, db1x_pmattr_show,		\
+				db1x_pmattr_store);
+
+ATTR(gpio0)		/* GPIO-based wakeup enable */
+ATTR(gpio1)
+ATTR(gpio2)
+ATTR(gpio3)
+ATTR(gpio4)
+ATTR(gpio5)
+ATTR(gpio6)
+ATTR(gpio7)
+ATTR(timer)		/* TOYMATCH2-based wakeup enable */
+ATTR(timer_timeout)	/* timer-based wakeup timeout value, in seconds */
+ATTR(uptime)		/* contents of SYS_TOYREAD */
+ATTR(wakesrc)		/* contents of SYS_WAKESRC after last wakeup */
+ATTR(wakemsk)		/* direct access to SYS_WAKEMSK */
+
+#define ATTR_LIST(x)	& x ## _attribute.attr
+static struct attribute *db1x_pmattrs[] = {
+	ATTR_LIST(gpio0),
+	ATTR_LIST(gpio1),
+	ATTR_LIST(gpio2),
+	ATTR_LIST(gpio3),
+	ATTR_LIST(gpio4),
+	ATTR_LIST(gpio5),
+	ATTR_LIST(gpio6),
+	ATTR_LIST(gpio7),
+	ATTR_LIST(timer),
+	ATTR_LIST(timer_timeout),
+	ATTR_LIST(uptime),
+	ATTR_LIST(wakesrc),
+	ATTR_LIST(wakemsk),
+	NULL,		/* terminator */
 };
 
-static struct ctl_table pm_dir_table[] = {
-	{
-		.ctl_name	= CTL_UNNUMBERED,
-		.procname	= "pm",
-		.mode		= 0555,
-		.child		= pm_table
-	},
-	{}
+static struct attribute_group db1x_pmattr_group = {
+	.name	= "db1x",
+	.attrs	= db1x_pmattrs,
 };
 
 /*
@@ -614,14 +555,16 @@ static int __init pm_init(void)
 	au_writel(0, SYS_TOYWRITE);
 	au_sync();
 
+	db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
+
 	au_writel(0, SYS_WAKESRC);
 	au_sync();
 	au_writel(0, SYS_WAKEMSK);
 	au_sync();
 
-	register_sysctl_table(pm_dir_table);
+	suspend_set_ops(&db1x_pm_ops);
 
-	return 0;
+	return sysfs_create_group(power_kobj, &db1x_pmattr_group);
 }
 
 __initcall(pm_init);
diff --git a/include/asm-mips/mach-au1x00/au1000.h b/include/asm-mips/mach-au1x00/au1000.h
index 7245960..639480e 100644
--- a/include/asm-mips/mach-au1x00/au1000.h
+++ b/include/asm-mips/mach-au1x00/au1000.h
@@ -1561,6 +1561,10 @@ enum soc_au1200_ints {
 #define SYS_SLPPWR		0xB1900078
 #define SYS_SLEEP		0xB190007C
 
+#define SYS_WAKEMSK_D2		(1 << 9)
+#define SYS_WAKEMSK_M2		(1 << 8)
+#define SYS_WAKEMSK_GPIO(x)	(1 << (x))
+
 /* Clock Controller */
 #define SYS_FREQCTRL0		0xB1900020
 #  define SYS_FC_FRDIV2_BIT	22
-- 
1.5.6.3

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

* Re: [PATCH v4 0/10] Alchemy updates.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (9 preceding siblings ...)
  2008-07-29 17:05 ` [PATCH v4 10/10] Alchemy: new demoboard userspace suspend interface Manuel Lauss
@ 2008-07-29 21:33 ` Kevin Hickey
  2008-07-30 10:28   ` Manuel Lauss
  2008-08-06 20:39 ` Kevin Hickey
  11 siblings, 1 reply; 18+ messages in thread
From: Kevin Hickey @ 2008-07-29 21:33 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: linux-mips

Manuel,

Thanks for sending these out again.  I've patched my tree (an up-to-date
git of 2.6.27-rc1) and have some preliminary feedback:
1.  I like the interface you added in patch #10.  Much better than the
old /proc one and flexible enough for a lot of different boards.  I
agree with your own comment that maybe it should be in the
board-specific directories so that people can name the nodes better, but
for now I think this is great.

2.  If I use the db1200_defconfig and enable Power Management
(CONFIG_PM), the build fails on the Au1xxx IDE and fb drivers.  Are you
seeing this too?  I see no reason to reject this patch if they don't
build with CONFIG_PM, I just want to make sure I'm not doing something
wrong.

3.  In my preliminary testing, the system was able to suspend and resume
correctly on a DB1200 board.  I will do some stress testing in the next
couple of days to make sure that it is stable in the long term. 

Thanks again.
-Kevin


On Tue, 2008-07-29 at 18:58 +0200, Manuel Lauss wrote:
> Hello,
> 
> Here's again a new set of patches to modernize Alchemy setup and PM code.
> All patches have been compile-tested with db1100 and db1200 defconfigs,
> and have been runnning on a few custom Au1250 boards for now more than
> 5 weeks.  I've suspended and resumed a few hundred times while stressing
> the system (continuous SD + CF reads while playing some wave files and
> compiling sources).
> 
> #1 removes unused functions
> #2 removes the cpu_table and replaces it with simpler code (IMHO of course)
> #3 enables use of cp0 counter as a fallback,
> #4 clockevent/clocksource support using one of the 2 counters of the Au1xxx
>    this also enables the use of the 'wait' instruction; depends on #3
> #5 cleanup made possible with #4 
> #7 and #8 fix suspend/resume.
> #9 adds DBDMA suspend/resume support.
> #10 replaces sysctl suspend interface with something better (IMO).
> 
> All patches depend on each other, have been run-tested on a custom AU1200
> system and compile-tested with a minimal config on db1100 and db1200.
> 
> Changes V3->V4:
> - rediffed against 2.6.27-rc1
> - add patch #10.
> 
> Changes V2->V3:
> - swap patches 1 and 2 
> - minor refinements, no function changes.
> 
> Changes V1->V2:
> - address Sergei's comments wrt. config[OD] handling
> - change TOY clocksource to RTC clocksource
> - add another patch (#5)
> 
> 
>  arch/mips/Kconfig                     |    8 
>  arch/mips/au1000/Kconfig              |    4 
>  arch/mips/au1000/common/Makefile      |    4 
>  arch/mips/au1000/common/clocks.c      |   65 +++--
>  arch/mips/au1000/common/cputable.c    |   52 ----
>  arch/mips/au1000/common/dbdma.c       |   65 +++++
>  arch/mips/au1000/common/dbg_io.c      |    4 
>  arch/mips/au1000/common/irq.c         |   57 ----
>  arch/mips/au1000/common/platform.c    |  257 ++++++++++++++++++++
>  arch/mips/au1000/common/power.c       |  421 ++++++----------------------------
>  arch/mips/au1000/common/setup.c       |   39 ---
>  arch/mips/au1000/common/sleeper.S     |  121 +++++----
>  arch/mips/au1000/common/time.c        |  305 ++++++++----------------
>  arch/mips/au1000/db1x00/Makefile      |    1 
>  arch/mips/au1000/mtx-1/Makefile       |    2 
>  arch/mips/au1000/pb1000/Makefile      |    1 
>  arch/mips/au1000/pb1100/Makefile      |    1 
>  arch/mips/au1000/pb1200/Makefile      |    2 
>  arch/mips/au1000/pb1500/Makefile      |    1 
>  arch/mips/au1000/pb1550/Makefile      |    1 
>  arch/mips/au1000/xxs1500/Makefile     |    1 
>  arch/mips/kernel/Makefile             |    4 
>  arch/mips/kernel/cevt-r4k.c           |    2 
>  arch/mips/kernel/csrc-r4k.c           |    2 
>  include/asm-mips/mach-au1x00/au1000.h |   64 +++--
>  include/asm-mips/time.h               |   24 +
>  26 files changed, 719 insertions(+), 789 deletions(-)
> 
> Thanks,
> 	Manuel Lauss
> 
-- 
Kevin Hickey
Alchemy Solutions
RMI Corporation
khickey@RMICorp.com
P: 512.691.8044

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

* Re: [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts.
  2008-07-29 17:03 ` [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts Manuel Lauss
@ 2008-07-30  7:42   ` Dmitri Vorobiev
  2008-07-30  8:54     ` Manuel Lauss
  0 siblings, 1 reply; 18+ messages in thread
From: Dmitri Vorobiev @ 2008-07-30  7:42 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: linux-mips

Manuel Lauss wrote:
> The Alchemy power.c file contains both core suspend/resume code, which
> is processor specific, and leftovers of the 2.4 PM userspace interface
> (sysctls).
> 
> This patch moves the userspace interface to the platform.c file and
> leaves the core board-independent suspend/resume parts which should be
> usable for all Alchemy-based systems intact.
> 
> Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
> ---
>  arch/mips/au1000/common/platform.c |  304 ++++++++++++++++++++++++++++++++++-
>  arch/mips/au1000/common/power.c    |  317 +-----------------------------------
>  2 files changed, 305 insertions(+), 316 deletions(-)
> 
> diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
> index 66d6770..1e89560 100644
> --- a/arch/mips/au1000/common/platform.c
> +++ b/arch/mips/au1000/common/platform.c
> @@ -6,6 +6,9 @@
>   * (C) Copyright Embedded Alley Solutions, Inc 2005
>   * Author: Pantelis Antoniou <pantelis@embeddedalley.com>
>   *
> + *  Some of the routines are right out of init/main.c, whose
> + *  copyrights apply here.
> + *
>   * This file is licensed under the terms of the GNU General Public
>   * License version 2.  This program is licensed "as is" without any
>   * warranty of any kind, whether express or implied.
> @@ -19,7 +22,11 @@
>  #include <linux/dma-mapping.h>
>  #include <linux/platform_device.h>
>  #include <linux/serial_8250.h>
> -#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/pm.h>
> +#include <linux/spinlock.h>
> +#include <linux/sysctl.h>
> +#include <linux/uaccess.h>
>  
>  #include <asm/mach-au1x00/au1xxx.h>
>  
> @@ -322,3 +329,298 @@ static int __init au1xxx_platform_init(void)
>  }
>  
>  arch_initcall(au1xxx_platform_init);
> +
> +
> +/*********************************************************************/
> +
> +
> +#ifdef CONFIG_PM
> +
> +static DEFINE_SPINLOCK(pm_lock);
> +
> +extern unsigned long save_local_and_disable(int controller);
> +extern void restore_local_and_enable(int controller, unsigned long mask);
> +extern void local_enable_irq(unsigned int irq_nr);
> +extern void au_sleep(void);
> +
> +/*
> + * Define this to cause the value you write to /proc/sys/pm/sleep to
> + * set the TOY timer for the amount of time you want to sleep.
> + * This is done mainly for testing, but may be useful in other cases.
> + * The value is number of 32KHz ticks to sleep.
> + */
> +#define SLEEP_TEST_TIMEOUT 1
> +#ifdef	SLEEP_TEST_TIMEOUT
> +static int sleep_ticks;
> +static void wakeup_counter0_set(int ticks)
> +{
> +	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
> +	au_sync();
> +}
> +#endif
> +
> +static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
> +		       void __user *buffer, size_t *len, loff_t *ppos)
> +{
> +	unsigned long wakeup, flags;
> +#ifdef SLEEP_TEST_TIMEOUT
> +#define TMPBUFLEN2 16
> +	char buf[TMPBUFLEN2], *p;
> +#endif
> +
> +	spin_lock_irqsave(&pm_lock, flags);
> +
> +	if (!write) {
> +		*len = 0;
> +		spin_unlock_irqrestore(&pm_lock, flags);
> +		return 0;
> +	}
> +
> +#ifdef SLEEP_TEST_TIMEOUT
> +	if (*len > TMPBUFLEN2 - 1)
> +		return -EFAULT;
> +	if (copy_from_user(buf, buffer, *len))
> +		return -EFAULT;
> +	buf[*len] = 0;
> +	p = buf;
> +	sleep_ticks = simple_strtoul(p, &p, 0);
> +#endif
> +
> +	/**
> +	 ** The code below is all system dependent and we should probably
> +	 ** have a function call out of here to set this up.  You need
> +	 ** to configure the GPIO or timer interrupts that will bring
> +	 ** you out of sleep.
> +	 ** For testing, the TOY counter wakeup is useful.
> +	 **/
> +#if 0

Why not simply remove the dead code while you're at it?

Thanks,
Dmitri

> +	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
> +
> +	/* GPIO 6 can cause a wake up event */
> +	wakeup = au_readl(SYS_WAKEMSK);
> +	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
> +	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
> +#else
> +	/* For testing, allow match20 to wake us up. */
> +#ifdef SLEEP_TEST_TIMEOUT
> +	wakeup_counter0_set(sleep_ticks);
> +#endif
> +	wakeup = 1 << 8;	/* turn on match20 wakeup   */
> +#endif
> +	au_writel(1, SYS_WAKESRC);	/* clear cause */
> +	au_sync();
> +	au_writel(wakeup, SYS_WAKEMSK);
> +	au_sync();
> +
> +	au_sleep();
> +
> +	spin_unlock_irqrestore(&pm_lock, flags);
> +
> +	return 0;
> +}
> +
> +/*
> + * This is the number of bits of precision for the loops_per_jiffy.
> + * Each bit takes on average 1.5/HZ seconds.  This (like the original)
> + * is a little better than 1%.
> + */
> +#define LPS_PREC 8
> +
> +static void au1000_calibrate_delay(void)
> +{
> +	unsigned long ticks, loopbit;
> +	int lps_precision = LPS_PREC;
> +
> +	loops_per_jiffy = 1 << 12;
> +
> +	while (loops_per_jiffy <<= 1) {
> +		/* Wait for "start of" clock tick */
> +		ticks = jiffies;
> +		while (ticks == jiffies)
> +			/* nothing */ ;
> +		/* Go ... */
> +		ticks = jiffies;
> +		__delay(loops_per_jiffy);
> +		ticks = jiffies - ticks;
> +		if (ticks)
> +			break;
> +	}
> +
> +	/*
> +	 * Do a binary approximation to get loops_per_jiffy set to be equal
> +	 * one clock (up to lps_precision bits)
> +	 */
> +	loops_per_jiffy >>= 1;
> +	loopbit = loops_per_jiffy;
> +	while (lps_precision-- && (loopbit >>= 1)) {
> +		loops_per_jiffy |= loopbit;
> +		ticks = jiffies;
> +		while (ticks == jiffies);
> +		ticks = jiffies;
> +		__delay(loops_per_jiffy);
> +		if (jiffies != ticks)	/* longer than 1 tick */
> +			loops_per_jiffy &= ~loopbit;
> +	}
> +}
> +
> +static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
> +		      void __user *buffer, size_t *len, loff_t *ppos)
> +{
> +	int retval = 0, i;
> +	unsigned long val, pll;
> +#define TMPBUFLEN 64
> +#define MAX_CPU_FREQ 396
> +	char buf[TMPBUFLEN], *p;
> +	unsigned long flags, intc0_mask, intc1_mask;
> +	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
> +	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
> +	unsigned long baud_rate;
> +
> +	spin_lock_irqsave(&pm_lock, flags);
> +	if (!write)
> +		*len = 0;
> +	else {
> +		/* Parse the new frequency */
> +		if (*len > TMPBUFLEN - 1) {
> +			spin_unlock_irqrestore(&pm_lock, flags);
> +			return -EFAULT;
> +		}
> +		if (copy_from_user(buf, buffer, *len)) {
> +			spin_unlock_irqrestore(&pm_lock, flags);
> +			return -EFAULT;
> +		}
> +		buf[*len] = 0;
> +		p = buf;
> +		val = simple_strtoul(p, &p, 0);
> +		if (val > MAX_CPU_FREQ) {
> +			spin_unlock_irqrestore(&pm_lock, flags);
> +			return -EFAULT;
> +		}
> +
> +		pll = val / 12;
> +		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
> +			/* Revisit this for higher speed CPUs */
> +			spin_unlock_irqrestore(&pm_lock, flags);
> +			return -EFAULT;
> +		}
> +
> +		old_baud_base = get_au1x00_uart_baud_base();
> +		old_cpu_freq = get_au1x00_speed();
> +
> +		new_cpu_freq = pll * 12 * 1000000;
> +	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
> +							    & 0x03) + 2) * 16));
> +		set_au1x00_speed(new_cpu_freq);
> +		set_au1x00_uart_baud_base(new_baud_base);
> +
> +		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
> +		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
> +			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
> +
> +		au_writel(pll, SYS_CPUPLL);
> +		au_sync_delay(1);
> +		au_writel(new_refresh, MEM_SDREFCFG);
> +		au_sync_delay(1);
> +
> +		for (i = 0; i < 4; i++)
> +			if (au_readl(UART_BASE + UART_MOD_CNTRL +
> +				     i * 0x00100000) == 3) {
> +				old_clk = au_readl(UART_BASE + UART_CLK +
> +						   i * 0x00100000);
> +				baud_rate = old_baud_base / old_clk;
> +				/*
> +				 * We won't get an exact baud rate and the error
> +				 * could be significant enough that our new
> +				 * calculation will result in a clock that will
> +				 * give us a baud rate that's too far off from
> +				 * what we really want.
> +				 */
> +				if (baud_rate > 100000)
> +					baud_rate = 115200;
> +				else if (baud_rate > 50000)
> +					baud_rate = 57600;
> +				else if (baud_rate > 30000)
> +					baud_rate = 38400;
> +				else if (baud_rate > 17000)
> +					baud_rate = 19200;
> +				else
> +					baud_rate = 9600;
> +				new_clk = new_baud_base / baud_rate;
> +				au_writel(new_clk, UART_BASE + UART_CLK +
> +					  i * 0x00100000);
> +				au_sync_delay(10);
> +			}
> +	}
> +
> +	/*
> +	 * We don't want _any_ interrupts other than match20. Otherwise our
> +	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
> +	 */
> +	intc0_mask = save_local_and_disable(0);
> +	intc1_mask = save_local_and_disable(1);
> +	local_enable_irq(AU1000_TOY_MATCH2_INT);
> +	spin_unlock_irqrestore(&pm_lock, flags);
> +	au1000_calibrate_delay();
> +	restore_local_and_enable(0, intc0_mask);
> +	restore_local_and_enable(1, intc1_mask);
> +
> +	return retval;
> +}
> +
> +
> +static struct ctl_table pm_table[] = {
> +	{
> +		.ctl_name	= CTL_UNNUMBERED,
> +		.procname	= "sleep",
> +		.data		= NULL,
> +		.maxlen		= 0,
> +		.mode		= 0600,
> +		.proc_handler	= &pm_do_sleep
> +	},
> +	{
> +		.ctl_name	= CTL_UNNUMBERED,
> +		.procname	= "freq",
> +		.data		= NULL,
> +		.maxlen		= 0,
> +		.mode		= 0600,
> +		.proc_handler	= &pm_do_freq
> +	},
> +	{}
> +};
> +
> +static struct ctl_table pm_dir_table[] = {
> +	{
> +		.ctl_name	= CTL_UNNUMBERED,
> +		.procname	= "pm",
> +		.mode		= 0555,
> +		.child		= pm_table
> +	},
> +	{}
> +};
> +
> +/*
> + * Initialize power interface
> + */
> +static int __init pm_init(void)
> +{
> +	/* init TOY to tick at 1Hz. No need to wait for access bits
> +	 * since there's plenty of time between here and the first
> +	 * suspend cycle.
> +	 */
> +	au_writel(32767, SYS_TOYTRIM);
> +	au_writel(0, SYS_TOYWRITE);
> +	au_sync();
> +
> +	au_writel(0, SYS_WAKESRC);
> +	au_sync();
> +	au_writel(0, SYS_WAKEMSK);
> +	au_sync();
> +
> +	register_sysctl_table(pm_dir_table);
> +
> +	return 0;
> +}
> +
> +__initcall(pm_init);
> +
> +#endif	/* CONFIG_PM */
> diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
> index 462ab21..4b0f6a1 100644
> --- a/arch/mips/au1000/common/power.c
> +++ b/arch/mips/au1000/common/power.c
> @@ -5,9 +5,6 @@
>   * Copyright 2001, 2008 MontaVista Software Inc.
>   * Author: MontaVista Software, Inc. <source@mvista.com>
>   *
> - *  Some of the routines are right out of init/main.c, whose
> - *  copyrights apply here.
> - *
>   *  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 of the	License, or (at your
> @@ -30,30 +27,12 @@
>   */
>  
>  #include <linux/init.h>
> -#include <linux/pm.h>
> -#include <linux/sysctl.h>
> -#include <linux/jiffies.h>
> -
> -#include <asm/uaccess.h>
>  #include <asm/cacheflush.h>
>  #include <asm/mach-au1x00/au1000.h>
>  
>  #ifdef CONFIG_PM
>  
> -#define DEBUG 1
> -#ifdef	DEBUG
> -#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __func__, ## args)
> -#else
> -#define DPRINTK(fmt, args...)
> -#endif
> -
> -static void au1000_calibrate_delay(void);
> -
> -extern unsigned long save_local_and_disable(int controller);
> -extern void restore_local_and_enable(int controller, unsigned long mask);
> -extern void local_enable_irq(unsigned int irq_nr);
> -
> -static DEFINE_SPINLOCK(pm_lock);
> +extern void save_and_sleep(void);
>  
>  /*
>   * We need to save/restore a bunch of core registers that are
> @@ -77,22 +56,6 @@ static unsigned int	sleep_usbhost_enable;
>  static unsigned int	sleep_usbdev_enable;
>  static unsigned int	sleep_static_memctlr[4][3];
>  
> -/*
> - * Define this to cause the value you write to /proc/sys/pm/sleep to
> - * set the TOY timer for the amount of time you want to sleep.
> - * This is done mainly for testing, but may be useful in other cases.
> - * The value is number of 32KHz ticks to sleep.
> - */
> -#define SLEEP_TEST_TIMEOUT 1
> -#ifdef	SLEEP_TEST_TIMEOUT
> -static int sleep_ticks;
> -static void wakeup_counter0_set(int ticks)
> -{
> -	au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2);
> -	au_sync();
> -}
> -#endif
> -
>  static void save_core_regs(void)
>  {
>  	extern void save_au1xxx_intctl(void);
> @@ -190,287 +153,11 @@ static void restore_core_regs(void)
>  	restore_au1xxx_intctl();
>  }
>  
> -unsigned long suspend_mode;
> -
> -void wakeup_from_suspend(void)
> -{
> -	suspend_mode = 0;
> -}
> -
> -int au_sleep(void)
> +void au_sleep(void)
>  {
> -	unsigned long wakeup, flags;
> -	extern void save_and_sleep(void);
> -
> -	spin_lock_irqsave(&pm_lock, flags);
> -
>  	save_core_regs();
> -
>  	flush_cache_all();
> -
> -	/**
> -	 ** The code below is all system dependent and we should probably
> -	 ** have a function call out of here to set this up.  You need
> -	 ** to configure the GPIO or timer interrupts that will bring
> -	 ** you out of sleep.
> -	 ** For testing, the TOY counter wakeup is useful.
> -	 **/
> -#if 0
> -	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
> -
> -	/* GPIO 6 can cause a wake up event */
> -	wakeup = au_readl(SYS_WAKEMSK);
> -	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
> -	wakeup |= 1 << 6;	/* turn on  GPIO  6 wakeup */
> -#else
> -	/* For testing, allow match20 to wake us up. */
> -#ifdef SLEEP_TEST_TIMEOUT
> -	wakeup_counter0_set(sleep_ticks);
> -#endif
> -	wakeup = 1 << 8;	/* turn on match20 wakeup   */
> -	wakeup = 0;
> -#endif
> -	au_writel(1, SYS_WAKESRC);	/* clear cause */
> -	au_sync();
> -	au_writel(wakeup, SYS_WAKEMSK);
> -	au_sync();
> -
>  	save_and_sleep();
> -
> -	/*
> -	 * After a wakeup, the cpu vectors back to 0x1fc00000, so
> -	 * it's up to the boot code to get us back here.
> -	 */
>  	restore_core_regs();
> -	spin_unlock_irqrestore(&pm_lock, flags);
> -	return 0;
> -}
> -
> -static int pm_do_sleep(ctl_table *ctl, int write, struct file *file,
> -		       void __user *buffer, size_t *len, loff_t *ppos)
> -{
> -#ifdef SLEEP_TEST_TIMEOUT
> -#define TMPBUFLEN2 16
> -	char buf[TMPBUFLEN2], *p;
> -#endif
> -
> -	if (!write)
> -		*len = 0;
> -	else {
> -#ifdef SLEEP_TEST_TIMEOUT
> -		if (*len > TMPBUFLEN2 - 1)
> -			return -EFAULT;
> -		if (copy_from_user(buf, buffer, *len))
> -			return -EFAULT;
> -		buf[*len] = 0;
> -		p = buf;
> -		sleep_ticks = simple_strtoul(p, &p, 0);
> -#endif
> -
> -		au_sleep();
> -	}
> -	return 0;
> -}
> -
> -static int pm_do_freq(ctl_table *ctl, int write, struct file *file,
> -		      void __user *buffer, size_t *len, loff_t *ppos)
> -{
> -	int retval = 0, i;
> -	unsigned long val, pll;
> -#define TMPBUFLEN 64
> -#define MAX_CPU_FREQ 396
> -	char buf[TMPBUFLEN], *p;
> -	unsigned long flags, intc0_mask, intc1_mask;
> -	unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh;
> -	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
> -	unsigned long baud_rate;
> -
> -	spin_lock_irqsave(&pm_lock, flags);
> -	if (!write)
> -		*len = 0;
> -	else {
> -		/* Parse the new frequency */
> -		if (*len > TMPBUFLEN - 1) {
> -			spin_unlock_irqrestore(&pm_lock, flags);
> -			return -EFAULT;
> -		}
> -		if (copy_from_user(buf, buffer, *len)) {
> -			spin_unlock_irqrestore(&pm_lock, flags);
> -			return -EFAULT;
> -		}
> -		buf[*len] = 0;
> -		p = buf;
> -		val = simple_strtoul(p, &p, 0);
> -		if (val > MAX_CPU_FREQ) {
> -			spin_unlock_irqrestore(&pm_lock, flags);
> -			return -EFAULT;
> -		}
> -
> -		pll = val / 12;
> -		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
> -			/* Revisit this for higher speed CPUs */
> -			spin_unlock_irqrestore(&pm_lock, flags);
> -			return -EFAULT;
> -		}
> -
> -		old_baud_base = get_au1x00_uart_baud_base();
> -		old_cpu_freq = get_au1x00_speed();
> -
> -		new_cpu_freq = pll * 12 * 1000000;
> -	        new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)
> -							    & 0x03) + 2) * 16));
> -		set_au1x00_speed(new_cpu_freq);
> -		set_au1x00_uart_baud_base(new_baud_base);
> -
> -		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
> -		new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) |
> -			      (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
> -
> -		au_writel(pll, SYS_CPUPLL);
> -		au_sync_delay(1);
> -		au_writel(new_refresh, MEM_SDREFCFG);
> -		au_sync_delay(1);
> -
> -		for (i = 0; i < 4; i++)
> -			if (au_readl(UART_BASE + UART_MOD_CNTRL +
> -				     i * 0x00100000) == 3) {
> -				old_clk = au_readl(UART_BASE + UART_CLK +
> -						   i * 0x00100000);
> -				baud_rate = old_baud_base / old_clk;
> -				/*
> -				 * We won't get an exact baud rate and the error
> -				 * could be significant enough that our new
> -				 * calculation will result in a clock that will
> -				 * give us a baud rate that's too far off from
> -				 * what we really want.
> -				 */
> -				if (baud_rate > 100000)
> -					baud_rate = 115200;
> -				else if (baud_rate > 50000)
> -					baud_rate = 57600;
> -				else if (baud_rate > 30000)
> -					baud_rate = 38400;
> -				else if (baud_rate > 17000)
> -					baud_rate = 19200;
> -				else
> -					baud_rate = 9600;
> -				new_clk = new_baud_base / baud_rate;
> -				au_writel(new_clk, UART_BASE + UART_CLK +
> -					  i * 0x00100000);
> -				au_sync_delay(10);
> -			}
> -	}
> -
> -	/*
> -	 * We don't want _any_ interrupts other than match20. Otherwise our
> -	 * au1000_calibrate_delay() calculation will be off, potentially a lot.
> -	 */
> -	intc0_mask = save_local_and_disable(0);
> -	intc1_mask = save_local_and_disable(1);
> -	local_enable_irq(AU1000_TOY_MATCH2_INT);
> -	spin_unlock_irqrestore(&pm_lock, flags);
> -	au1000_calibrate_delay();
> -	restore_local_and_enable(0, intc0_mask);
> -	restore_local_and_enable(1, intc1_mask);
> -
> -	return retval;
> -}
> -
> -
> -static struct ctl_table pm_table[] = {
> -	{
> -		.ctl_name	= CTL_UNNUMBERED,
> -		.procname	= "sleep",
> -		.data		= NULL,
> -		.maxlen		= 0,
> -		.mode		= 0600,
> -		.proc_handler	= &pm_do_sleep
> -	},
> -	{
> -		.ctl_name	= CTL_UNNUMBERED,
> -		.procname	= "freq",
> -		.data		= NULL,
> -		.maxlen		= 0,
> -		.mode		= 0600,
> -		.proc_handler	= &pm_do_freq
> -	},
> -	{}
> -};
> -
> -static struct ctl_table pm_dir_table[] = {
> -	{
> -		.ctl_name	= CTL_UNNUMBERED,
> -		.procname	= "pm",
> -		.mode		= 0555,
> -		.child		= pm_table
> -	},
> -	{}
> -};
> -
> -/*
> - * Initialize power interface
> - */
> -static int __init pm_init(void)
> -{
> -	/* init TOY to tick at 1Hz. No need to wait for access bits
> -	 * since there's plenty of time between here and the first
> -	 * suspend cycle.
> -	 */
> -	au_writel(32767, SYS_TOYTRIM);
> -	au_writel(0, SYS_TOYWRITE);
> -	au_sync();
> -
> -	register_sysctl_table(pm_dir_table);
> -	return 0;
> -}
> -
> -__initcall(pm_init);
> -
> -/*
> - * This is right out of init/main.c
> - */
> -
> -/*
> - * This is the number of bits of precision for the loops_per_jiffy.
> - * Each bit takes on average 1.5/HZ seconds.  This (like the original)
> - * is a little better than 1%.
> - */
> -#define LPS_PREC 8
> -
> -static void au1000_calibrate_delay(void)
> -{
> -	unsigned long ticks, loopbit;
> -	int lps_precision = LPS_PREC;
> -
> -	loops_per_jiffy = 1 << 12;
> -
> -	while (loops_per_jiffy <<= 1) {
> -		/* Wait for "start of" clock tick */
> -		ticks = jiffies;
> -		while (ticks == jiffies)
> -			/* nothing */ ;
> -		/* Go ... */
> -		ticks = jiffies;
> -		__delay(loops_per_jiffy);
> -		ticks = jiffies - ticks;
> -		if (ticks)
> -			break;
> -	}
> -
> -	/*
> -	 * Do a binary approximation to get loops_per_jiffy set to be equal
> -	 * one clock (up to lps_precision bits)
> -	 */
> -	loops_per_jiffy >>= 1;
> -	loopbit = loops_per_jiffy;
> -	while (lps_precision-- && (loopbit >>= 1)) {
> -		loops_per_jiffy |= loopbit;
> -		ticks = jiffies;
> -		while (ticks == jiffies);
> -		ticks = jiffies;
> -		__delay(loops_per_jiffy);
> -		if (jiffies != ticks)	/* longer than 1 tick */
> -			loops_per_jiffy &= ~loopbit;
> -	}
>  }
>  #endif	/* CONFIG_PM */

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

* Re: [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts.
  2008-07-30  7:42   ` Dmitri Vorobiev
@ 2008-07-30  8:54     ` Manuel Lauss
  2008-07-30  9:02       ` Dmitri Vorobiev
  0 siblings, 1 reply; 18+ messages in thread
From: Manuel Lauss @ 2008-07-30  8:54 UTC (permalink / raw)
  To: Dmitri Vorobiev; +Cc: Manuel Lauss, linux-mips

Dmitri Vorobiev wrote:
> Manuel Lauss wrote:
>> The Alchemy power.c file contains both core suspend/resume code, which
>> is processor specific, and leftovers of the 2.4 PM userspace interface
>> (sysctls).
>>
>> This patch moves the userspace interface to the platform.c file and
>> leaves the core board-independent suspend/resume parts which should be
>> usable for all Alchemy-based systems intact.
>>
>> Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
>> ---
>>  arch/mips/au1000/common/platform.c |  304 ++++++++++++++++++++++++++++++++++-
>>  arch/mips/au1000/common/power.c    |  317 +-----------------------------------
>>  2 files changed, 305 insertions(+), 316 deletions(-)
>>
>> diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
>> index 66d6770..1e89560 100644
>> --- a/arch/mips/au1000/common/platform.c
>> +++ b/arch/mips/au1000/common/platform.c
[...]
>> +	/**
>> +	 ** The code below is all system dependent and we should probably
>> +	 ** have a function call out of here to set this up.  You need
>> +	 ** to configure the GPIO or timer interrupts that will bring
>> +	 ** you out of sleep.
>> +	 ** For testing, the TOY counter wakeup is useful.
>> +	 **/
>> +#if 0
> 
> Why not simply remove the dead code while you're at it?

That code is removed by a later patch in the series.  This patch is
just code reshuffling; I wanted to leave everything (as broken) as
it was.

Thanks,
	Manuel Lauss

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

* Re: [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts.
  2008-07-30  8:54     ` Manuel Lauss
@ 2008-07-30  9:02       ` Dmitri Vorobiev
  0 siblings, 0 replies; 18+ messages in thread
From: Dmitri Vorobiev @ 2008-07-30  9:02 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: Manuel Lauss, linux-mips

Manuel Lauss wrote:
> Dmitri Vorobiev wrote:
>> Manuel Lauss wrote:
>>> The Alchemy power.c file contains both core suspend/resume code, which
>>> is processor specific, and leftovers of the 2.4 PM userspace interface
>>> (sysctls).
>>>
>>> This patch moves the userspace interface to the platform.c file and
>>> leaves the core board-independent suspend/resume parts which should be
>>> usable for all Alchemy-based systems intact.
>>>
>>> Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
>>> ---
>>>  arch/mips/au1000/common/platform.c |  304
>>> ++++++++++++++++++++++++++++++++++-
>>>  arch/mips/au1000/common/power.c    |  317
>>> +-----------------------------------
>>>  2 files changed, 305 insertions(+), 316 deletions(-)
>>>
>>> diff --git a/arch/mips/au1000/common/platform.c
>>> b/arch/mips/au1000/common/platform.c
>>> index 66d6770..1e89560 100644
>>> --- a/arch/mips/au1000/common/platform.c
>>> +++ b/arch/mips/au1000/common/platform.c
> [...]
>>> +    /**
>>> +     ** The code below is all system dependent and we should probably
>>> +     ** have a function call out of here to set this up.  You need
>>> +     ** to configure the GPIO or timer interrupts that will bring
>>> +     ** you out of sleep.
>>> +     ** For testing, the TOY counter wakeup is useful.
>>> +     **/
>>> +#if 0
>>
>> Why not simply remove the dead code while you're at it?
> 
> That code is removed by a later patch in the series.

Oh, I haven't noticed that. My mistake. Sorry.

Thanks,
Dmitri

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

* Re: [PATCH v4 0/10] Alchemy updates.
  2008-07-29 21:33 ` [PATCH v4 0/10] Alchemy updates Kevin Hickey
@ 2008-07-30 10:28   ` Manuel Lauss
  2008-07-30 21:36     ` Kevin Hickey
  0 siblings, 1 reply; 18+ messages in thread
From: Manuel Lauss @ 2008-07-30 10:28 UTC (permalink / raw)
  To: Kevin Hickey; +Cc: Manuel Lauss, linux-mips

Hi Kevin,

> 1.  I like the interface you added in patch #10.  Much better than the
> old /proc one and flexible enough for a lot of different boards.  I
> agree with your own comment that maybe it should be in the
> board-specific directories so that people can name the nodes better, but
> for now I think this is great.

Thinking about my board here in particular: I need to save/restore a
few bytes in an FPGA (in the ->enter() callback) and call a few other
pm related callbacks; the gpio nodes are set internally by or'ing together
other wake sources (think carddetects, Wake on lan, GSM modem irq, ...).

So if we want to keep it the way it is now, we should give boards a means
to disable exposure of each of the "standard" wakesources of the Au1000 chip,
to provide their own nodes and suspend_ops_t callbacks.


> 2.  If I use the db1200_defconfig and enable Power Management
> (CONFIG_PM), the build fails on the Au1xxx IDE and fb drivers.  Are you
> seeing this too?  I see no reason to reject this patch if they don't
> build with CONFIG_PM, I just want to make sure I'm not doing something
> wrong.

The au1200fb failure, yes. I also have a patch to fix it, but it needs
a bit more love: X for example does not always survive the framebuffer
suspend/resume cycle (it complains about changed parameters after resume).

IDE I can't use so didn't test.


> 3.  In my preliminary testing, the system was able to suspend and resume
> correctly on a DB1200 board.  I will do some stress testing in the next
> couple of days to make sure that it is stable in the long term. 

Very much appreciated!

Thanks Kevin,
	Manuel Lauss

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

* Re: [PATCH v4 0/10] Alchemy updates.
  2008-07-30 10:28   ` Manuel Lauss
@ 2008-07-30 21:36     ` Kevin Hickey
  0 siblings, 0 replies; 18+ messages in thread
From: Kevin Hickey @ 2008-07-30 21:36 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: Manuel Lauss, linux-mips

On Wed, 2008-07-30 at 12:28 +0200, Manuel Lauss wrote:
> Hi Kevin,
> 
> > 1.  I like the interface you added in patch #10.  Much better than the
> > old /proc one and flexible enough for a lot of different boards.  I
> > agree with your own comment that maybe it should be in the
> > board-specific directories so that people can name the nodes better, but
> > for now I think this is great.
> 
> Thinking about my board here in particular: I need to save/restore a
> few bytes in an FPGA (in the ->enter() callback) and call a few other
> pm related callbacks; the gpio nodes are set internally by or'ing together
> other wake sources (think carddetects, Wake on lan, GSM modem irq, ...).
> 
> So if we want to keep it the way it is now, we should give boards a means
> to disable exposure of each of the "standard" wakesources of the Au1000 chip,
> to provide their own nodes and suspend_ops_t callbacks.
> 
I agree - what I should have said is that the current method is good for
this patch set.  It should be enhanced but that should not block the
acceptance of these patches.
> 
> > 2.  If I use the db1200_defconfig and enable Power Management
> > (CONFIG_PM), the build fails on the Au1xxx IDE and fb drivers.  Are you
> > seeing this too?  I see no reason to reject this patch if they don't
> > build with CONFIG_PM, I just want to make sure I'm not doing something
> > wrong.
> 
> The au1200fb failure, yes. I also have a patch to fix it, but it needs
> a bit more love: X for example does not always survive the framebuffer
> suspend/resume cycle (it complains about changed parameters after resume).
> 
Sounds good.  Looking forward to it.

> IDE I can't use so didn't test.
We have it on our board so I'll take care of it.  I have a partial
solution working already.

> 
> 
> > 3.  In my preliminary testing, the system was able to suspend and resume
> > correctly on a DB1200 board.  I will do some stress testing in the next
> > couple of days to make sure that it is stable in the long term. 
> 
> Very much appreciated!
> 
> Thanks Kevin,
> 	Manuel Lauss
> 
-- 
Kevin Hickey
Alchemy Solutions
RMI Corporation
khickey@RMICorp.com
P: 512.691.8044

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

* Re: [PATCH v4 0/10] Alchemy updates.
  2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
                   ` (10 preceding siblings ...)
  2008-07-29 21:33 ` [PATCH v4 0/10] Alchemy updates Kevin Hickey
@ 2008-08-06 20:39 ` Kevin Hickey
  11 siblings, 0 replies; 18+ messages in thread
From: Kevin Hickey @ 2008-08-06 20:39 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: linux-mips

Manuel et al.,

I've tested these patches on a DB1200 board and they look good.  I have
an Au1xxx IDE power management patch that depends on them that I can
release after they've been applied.

One catch - these no longer apply to the latest HEAD.  Manuel, can you
re-spin and re-post them one more time?

-Kevin



On Tue, 2008-07-29 at 18:58 +0200, Manuel Lauss wrote:
> Hello,
> 
> Here's again a new set of patches to modernize Alchemy setup and PM code.
> All patches have been compile-tested with db1100 and db1200 defconfigs,
> and have been runnning on a few custom Au1250 boards for now more than
> 5 weeks.  I've suspended and resumed a few hundred times while stressing
> the system (continuous SD + CF reads while playing some wave files and
> compiling sources).
> 
> #1 removes unused functions
> #2 removes the cpu_table and replaces it with simpler code (IMHO of course)
> #3 enables use of cp0 counter as a fallback,
> #4 clockevent/clocksource support using one of the 2 counters of the Au1xxx
>    this also enables the use of the 'wait' instruction; depends on #3
> #5 cleanup made possible with #4 
> #7 and #8 fix suspend/resume.
> #9 adds DBDMA suspend/resume support.
> #10 replaces sysctl suspend interface with something better (IMO).
> 
> All patches depend on each other, have been run-tested on a custom AU1200
> system and compile-tested with a minimal config on db1100 and db1200.
> 
> Changes V3->V4:
> - rediffed against 2.6.27-rc1
> - add patch #10.
> 
> Changes V2->V3:
> - swap patches 1 and 2 
> - minor refinements, no function changes.
> 
> Changes V1->V2:
> - address Sergei's comments wrt. config[OD] handling
> - change TOY clocksource to RTC clocksource
> - add another patch (#5)
> 
> 
>  arch/mips/Kconfig                     |    8 
>  arch/mips/au1000/Kconfig              |    4 
>  arch/mips/au1000/common/Makefile      |    4 
>  arch/mips/au1000/common/clocks.c      |   65 +++--
>  arch/mips/au1000/common/cputable.c    |   52 ----
>  arch/mips/au1000/common/dbdma.c       |   65 +++++
>  arch/mips/au1000/common/dbg_io.c      |    4 
>  arch/mips/au1000/common/irq.c         |   57 ----
>  arch/mips/au1000/common/platform.c    |  257 ++++++++++++++++++++
>  arch/mips/au1000/common/power.c       |  421 ++++++----------------------------
>  arch/mips/au1000/common/setup.c       |   39 ---
>  arch/mips/au1000/common/sleeper.S     |  121 +++++----
>  arch/mips/au1000/common/time.c        |  305 ++++++++----------------
>  arch/mips/au1000/db1x00/Makefile      |    1 
>  arch/mips/au1000/mtx-1/Makefile       |    2 
>  arch/mips/au1000/pb1000/Makefile      |    1 
>  arch/mips/au1000/pb1100/Makefile      |    1 
>  arch/mips/au1000/pb1200/Makefile      |    2 
>  arch/mips/au1000/pb1500/Makefile      |    1 
>  arch/mips/au1000/pb1550/Makefile      |    1 
>  arch/mips/au1000/xxs1500/Makefile     |    1 
>  arch/mips/kernel/Makefile             |    4 
>  arch/mips/kernel/cevt-r4k.c           |    2 
>  arch/mips/kernel/csrc-r4k.c           |    2 
>  include/asm-mips/mach-au1x00/au1000.h |   64 +++--
>  include/asm-mips/time.h               |   24 +
>  26 files changed, 719 insertions(+), 789 deletions(-)
> 
> Thanks,
> 	Manuel Lauss
> 
-- 
Kevin Hickey
Alchemy Solutions
RMI Corporation
khickey@RMICorp.com
P: 512.691.8044

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

end of thread, other threads:[~2008-08-06 20:38 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-29 16:58 [PATCH v4 0/10] Alchemy updates Manuel Lauss
2008-07-29 16:59 ` [PATCH v4 1/10] Alchemy: remove get/set_au1x00_lcd_clock() Manuel Lauss
2008-07-29 17:00 ` [PATCH v4 2/10] Alchemy: remove cpu_table Manuel Lauss
2008-07-29 17:00 ` [PATCH v4 3/10] MIPS: make cp0 counter clocksource/event usable as fallback Manuel Lauss
2008-07-29 17:01 ` [PATCH v4 4/10] Alchemy: RTC counter clocksource / clockevent support Manuel Lauss
2008-07-29 17:02 ` [PATCH v4 5/10] Alchemy: move calc_clock function Manuel Lauss
2008-07-29 17:02 ` [PATCH v4 6/10] Alchemy: compile platform.c only when building for a demoboard Manuel Lauss
2008-07-29 17:03 ` [PATCH v4 7/10] Alchemy: split core PM code from sysctl parts Manuel Lauss
2008-07-30  7:42   ` Dmitri Vorobiev
2008-07-30  8:54     ` Manuel Lauss
2008-07-30  9:02       ` Dmitri Vorobiev
2008-07-29 17:04 ` [PATCH v4 8/10] Alchemy: Fix PM code for Au1200/Au1550 Manuel Lauss
2008-07-29 17:04 ` [PATCH v4 9/10] Alchemy: dbdma suspend/resume support Manuel Lauss
2008-07-29 17:05 ` [PATCH v4 10/10] Alchemy: new demoboard userspace suspend interface Manuel Lauss
2008-07-29 21:33 ` [PATCH v4 0/10] Alchemy updates Kevin Hickey
2008-07-30 10:28   ` Manuel Lauss
2008-07-30 21:36     ` Kevin Hickey
2008-08-06 20:39 ` Kevin Hickey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox