All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] lite5200b low power mode
@ 2006-12-22 10:54 Domen Puncer
  2006-12-22 11:10 ` Sylvain Munaut
  2007-01-23  9:33 ` [RFC PATCHES] lite5200b low power mode works Domen Puncer
  0 siblings, 2 replies; 7+ messages in thread
From: Domen Puncer @ 2006-12-22 10:54 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi!

I have managed to implement low power mode... well, to some
degree, at least.

Short description:
patch u-boot, it's the return from wakeup code. pretty clean.
	address is saved at 0x0 (0xc000_0000)
patch linux with PM-platform_bus-and-late_suspend-early_resume.patch
	didn't investigate too much, but it seems to broke mine.
patch linux with lite5200b_low_power.patch
	ugly, with debugging, hacks and whatever

Boot Linux and 'echo mem > /sys/power/state'.
It won't work on PCI, possibly other devices, PSC0 and FEC seem
to work ok for me.

It seems to work reliably (checksums match, so ram was in self-refresh)
when called from boot scripts, but not so when from console prompt.


If someone's interested (or maybe wants to play with this during
holiday? ;-) ), more info follow:


Intro
-----
lite5200b has low power mode, which turns off supply to MPC5200.
It is controlled via "...QT1" microcontroller (PSC2_4 pin on mpc)

The cpu itself also supports a similar mode - deep sleep, where
cpu is stopped but contents remain because it's static.

Vital for restore in both cases is putting sdram in self-refresh
correctly. For deep sleep mpc5200b does that automatically
(AN3045 says it's so for B rev., and tests confirm it).


self-refresh
------------
There are many ways of putting ram in self-refresh.
Code can't be executed from SDRAM, but there are a few choices:
FLASH, SRAM (don't forget & ~_PAGE_GUARDED in mpc52xx_map_io),
and instruction cache (prefetching via icbi or as described
in g2 core reference manual didn't work for me. However,
executing code surely did put it in cache).

Now, the sad part for me, is that I never actually managed
to put it in SR. I've bugged Freescale, and they've been
very helpfull, even tried my code on another project/kernel,
and it worked.
Maybe I was doing something wrong, maybe something was accessing
SDRAM (i flushed data cache, disabled interrupts).

The trick this patch uses, it to signal "low power" to "QT1",
and after that put mpc5200b in deep sleep.
As I'm figuring out now, this isn't 100% ok either. I think
it always worked right when called from boot scripts. Calling it
from serial console is pretty much unpredictible.


deep sleep
----------
And a note about deep sleep.
I had code to put mpc in deep sleep in SRAM (so it should remain
here on wakeup, to wake SDRAM). 1st stage wakeup code was
interrupt handler (0x500), put in i-cache, by forcing an
interrupt before sleeping.
It went to sleep, but on restore, I didn't get further than
i-cached wakeup code. Jump back to SRAM failed :-(


Hopefully someone finds this useful.


I'd appreaciate any tips, ideas, patches etc. :-)


	Domen

[-- Attachment #2: u-boot.git.qt_sleep_in_c.patch --]
[-- Type: text/plain, Size: 2199 bytes --]

diff --git a/board/icecube/icecube.c b/board/icecube/icecube.c
index 4f056b2..741f1b5 100644
--- a/board/icecube/icecube.c
+++ b/board/icecube/icecube.c
@@ -38,6 +38,52 @@ # else
 #include "mt48lc16m16a2-75.h"
 # endif
 #endif
+
+#ifdef CONFIG_LITE5200B
+/* u-boot part of low-power mode implementation */
+#define SAVED_ADDR 0x00000000
+void lite5200b_wakeup(void)
+{
+	unsigned char wakeup_pin;
+	void (*linux_wakeup)(void);
+
+	/* check PSC2_4, if it's down "QT" is signaling we have a wakeup
+	 * from low power mode */
+	*(vu_char *)MPC5XXX_WU_GPIO_ENABLE = 0x02;
+	__asm__ volatile ("sync");
+
+	wakeup_pin = *(vu_char *)MPC5XXX_WU_GPIO_DATA_I;
+	if (wakeup_pin & 0x02)
+		return;
+
+	/* acknowledge to "QT"
+	 * by holding pin at 1 for 10 uS */
+	*(vu_char *)MPC5XXX_WU_GPIO_DIR = 0x02;
+	__asm__ volatile ("sync");
+	*(vu_char *)MPC5XXX_WU_GPIO_DATA = 0x02;
+	__asm__ volatile ("sync");
+	udelay(10);
+
+	/* put ram out of self-refresh */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL |= 0x80000000;	// mode_en
+	__asm__ volatile ("sync");
+	*(vu_long *)MPC5XXX_SDRAM_CTRL |= 0x50000000;	// cke ref_en
+	__asm__ volatile ("sync");
+	*(vu_long *)MPC5XXX_SDRAM_CTRL &= ~0x80000000;	// !mode_en
+	__asm__ volatile ("sync");
+	udelay(10); /* wait a bit */
+
+	/* jump back to linux kernel code */
+	linux_wakeup = *(unsigned long *)SAVED_ADDR;
+	printf("\n\nLooks like we just woke, transferring control to 0x%08lx\n",
+			linux_wakeup);
+	linux_wakeup();
+}
+#else
+#define lite5200b_wakeup
+#endif
+
+
 #ifndef CFG_RAMBOOT
 static void sdram_start (int hi_addr)
 {
@@ -204,6 +250,8 @@ #endif /* CFG_RAMBOOT */
 		__asm__ volatile ("sync");
 	}
 
+	lite5200b_wakeup();
+
 	return dramsize + dramsize2;
 }
 
diff --git a/include/mpc5xxx.h b/include/mpc5xxx.h
index 1d20d1d..c7eb090 100644
--- a/include/mpc5xxx.h
+++ b/include/mpc5xxx.h
@@ -189,6 +189,7 @@ #define MPC5XXX_WU_GPIO_ENABLE  (MPC5XXX
 #define MPC5XXX_WU_GPIO_ODE     (MPC5XXX_WU_GPIO + 0x0004)
 #define MPC5XXX_WU_GPIO_DIR     (MPC5XXX_WU_GPIO + 0x0008)
 #define MPC5XXX_WU_GPIO_DATA    (MPC5XXX_WU_GPIO + 0x000c)
+#define MPC5XXX_WU_GPIO_DATA_I  (MPC5XXX_WU_GPIO + 0x0020)
 
 /* PCI registers */
 #define MPC5XXX_PCI_CMD		(MPC5XXX_PCI + 0x04)

[-- Attachment #3: PM-platform_bus-and-late_suspend-early_resume.patch --]
[-- Type: text/plain, Size: 4060 bytes --]

commit 386415d88b1ae50304f9c61aa3e0db082fa90428
Author: David Brownell <david-b@pacbell.net>
Date:   Sun Sep 3 13:16:45 2006 -0700

    PM: platform_bus and late_suspend/early_resume
    
    Teach platform_bus about the new suspend_late/resume_early PM calls,
    issued with IRQs off.  Do we really need sysdev and friends any more,
    or can janitors start switching its users over to platform_device so
    we can do a minor code-ectomy?
    
    Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -505,36 +505,12 @@ static int platform_match(struct device 
 	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
 }
 
-static int platform_suspend(struct device *dev, pm_message_t mesg)
+static int platform_suspend(struct device * dev, pm_message_t state)
 {
 	int ret = 0;
 
 	if (dev->driver && dev->driver->suspend)
-		ret = dev->driver->suspend(dev, mesg);
-
-	return ret;
-}
-
-static int platform_suspend_late(struct device *dev, pm_message_t mesg)
-{
-	struct platform_driver *drv = to_platform_driver(dev->driver);
-	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
-	int ret = 0;
-
-	if (dev->driver && drv->suspend_late)
-		ret = drv->suspend_late(pdev, mesg);
-
-	return ret;
-}
-
-static int platform_resume_early(struct device *dev)
-{
-	struct platform_driver *drv = to_platform_driver(dev->driver);
-	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
-	int ret = 0;
-
-	if (dev->driver && drv->resume_early)
-		ret = drv->resume_early(pdev);
+		ret = dev->driver->suspend(dev, state);
 
 	return ret;
 }
@@ -555,8 +531,6 @@ struct bus_type platform_bus_type = {
 	.match		= platform_match,
 	.uevent		= platform_uevent,
 	.suspend	= platform_suspend,
-	.suspend_late	= platform_suspend_late,
-	.resume_early	= platform_resume_early,
 	.resume		= platform_resume,
 };
 EXPORT_SYMBOL_GPL(platform_bus_type);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c
diff --git a/drivers/video/cfbfillrect.c b/drivers/video/cfbfillrect.c
diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c
diff --git a/include/asm-ppc/mpc52xx.h b/include/asm-ppc/mpc52xx.h
diff --git a/include/linux/fb.h b/include/linux/fb.h
diff --git a/include/linux/ide.h b/include/linux/ide.h
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 29cd6de..782090c 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -49,8 +49,6 @@ struct platform_driver {
 	int (*remove)(struct platform_device *);
 	void (*shutdown)(struct platform_device *);
 	int (*suspend)(struct platform_device *, pm_message_t state);
-	int (*suspend_late)(struct platform_device *, pm_message_t state);
-	int (*resume_early)(struct platform_device *);
 	int (*resume)(struct platform_device *);
 	struct device_driver driver;
 };
diff --git a/kernel/power/main.c b/kernel/power/main.c
diff --git a/sound/Kconfig b/sound/Kconfig
diff --git a/sound/Makefile b/sound/Makefile

[-- Attachment #4: lite5200b_low_power.patch --]
[-- Type: text/plain, Size: 29703 bytes --]

Index: work-powerpc.git/arch/ppc/platforms/Makefile
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/Makefile
+++ work-powerpc.git/arch/ppc/platforms/Makefile
@@ -28,6 +28,10 @@ obj-$(CONFIG_SANDPOINT)		+= sandpoint.o
 obj-$(CONFIG_SBC82xx)		+= sbc82xx.o
 obj-$(CONFIG_SPRUCE)		+= spruce.o
 obj-$(CONFIG_LITE5200)		+= lite5200.o
+ifeq ($(CONFIG_LITE5200),y)
+obj-$(CONFIG_PM)		+= lite5200_pm.o
+obj-$(CONFIG_PM)		+= lite5200_sleep.o
+endif
 obj-$(CONFIG_MEDIA5200)		+= media5200.o
 obj-$(CONFIG_EV64360)		+= ev64360.o
 obj-$(CONFIG_MPC86XADS)		+= mpc866ads_setup.o
Index: work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
@@ -0,0 +1,141 @@
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/machdep.h>
+
+extern void l5200_sleep(void);
+extern void l5200_qt_sleep(void);
+
+static int l5200_pm_valid(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_MEM:
+	case PM_SUSPEND_STANDBY:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int l5200_pm_prepare(suspend_state_t state)
+{
+	return 0;
+}
+
+extern void mpc52xx_pic_suspend(void);
+extern void mpc52xx_pic_resume(void);
+
+static int l5200_pm_enter(suspend_state_t state)
+{
+	struct mpc52xx_cdm __iomem *cdm;
+	struct mpc52xx_gpio __iomem *gpio;
+	unsigned long *start = (unsigned long *)0xc0700000;
+	unsigned long length = 0x1000000; // 16*4 mb
+
+	u32 gpio_saved_pc;
+
+	printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+
+	/* Map zones */
+	cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
+	gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE);
+
+	if (!cdm || !gpio) {
+		printk(KERN_ALERT "erroring out %s: %i\n", __func__, __LINE__);
+		return 1;
+	}
+	cdm->ccs_sleep_enable = 0x01;	/* enable CSS (clock control sequencer)*/
+	__asm__ __volatile__ ("sync");
+
+	/* save ie. ethernet pins configuration */
+	gpio_saved_pc = gpio->port_config;
+
+#if 0
+	gpio->port_config  &= 0xff0fffff;
+	gpio->simple_gpioe |= 0x30000000;
+	gpio->simple_ddr   |= 0x30000000;
+	gpio->simple_dvo   = 0x30000000;
+	__asm__ __volatile__ ("sync");
+#endif
+	iounmap(cdm);
+	iounmap(gpio);
+
+	mpc52xx_pic_suspend();
+	{
+		unsigned long checksum = 0;
+		int i;
+		for (i=0; i<length; i++)
+			checksum += start[i];
+		printk(KERN_ALERT "%s: %i; checksum: 0x%lx\n", __func__, __LINE__, checksum);
+//		*(unsigned int*)0xc0000010 = checksum;
+
+		/*for (i=0; i<0x3ac0/4; i+=16/4) {
+			unsigned __iomem *io;
+			io = i + (unsigned __iomem *)0xf0000000;
+			printk(KERN_ALERT "%p:  %08x %08x %08x %08x\n",
+					io, in_be32(io), in_be32(io+4),
+					in_be32(io+8), in_be32(io+0xc));
+		}*/
+	}
+
+	if (state == PM_SUSPEND_MEM) {
+		l5200_qt_sleep();
+	}
+	{
+		unsigned long checksum = 0;
+		int i;
+		for (i=0; i<length; i++)
+			checksum += start[i];
+		printk(KERN_ALERT "%s: %i; checksum: 0x%lx\n", __func__, __LINE__, checksum);
+//		*(unsigned int*)0xc0000018 = checksum;
+
+		/*for (i=0; i<0x3ac0/4; i+=16/4) {
+			unsigned __iomem *io;
+			io = i + (unsigned __iomem *)0xf0000000;
+			printk(KERN_ALERT "%p:  %08x %08x %08x %08x\n",
+					io, in_be32(io), in_be32(io+4),
+					in_be32(io+8), in_be32(io+0xc));
+		}*/
+	}
+
+	gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE);
+	/* restore ie. ethernet pins configuration */
+	gpio->port_config = gpio_saved_pc;
+	iounmap(gpio);
+
+	ppc_md.setup_arch();
+	mpc52xx_pic_resume();
+
+	*((volatile char *)0xc0000008) = 0x2f;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
+
+	// red timer led
+	*((volatile int *)0xf0000630) = 0x24;
+	asm volatile("sync");
+
+	return 0;
+}
+
+static int l5200_pm_finish(suspend_state_t state)
+{
+	printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+	return 0;
+}
+
+static struct pm_ops lite5200_pm_ops = {
+	.valid		= l5200_pm_valid,
+	.prepare	= l5200_pm_prepare,
+	.enter		= l5200_pm_enter,
+	.finish		= l5200_pm_finish,
+};
+
+static int __init lite5200_pm_init(void)
+{
+	printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+	pm_set_ops(&lite5200_pm_ops);
+	return 0;
+}
+
+arch_initcall(lite5200_pm_init);
Index: work-powerpc.git/arch/ppc/platforms/lite5200_sleep.S
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/ppc/platforms/lite5200_sleep.S
@@ -0,0 +1,490 @@
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+
+#define MBAR	0xf0000000
+
+#define SDRAM_MODE	0x100
+#define SDRAM_CTRL	0x104
+#define SDRAM_CFG1	0x108
+#define SDRAM_CFG2	0x10c
+
+#define GPIO_CFG	0xb00
+#define GPIO_GPIOE	0xb04
+#define GPIO_ODE	0xb08
+#define GPIO_DDR	0xb0c
+#define GPIO_DVO	0xb10
+
+#define GPIOW_GPIOE	0xc00
+#define GPIOW_ODE	0xc04
+#define GPIOW_DDR	0xc08
+#define GPIOW_DVO	0xc0c
+#define GPIOW_INTEN	0xc10
+
+#define LED_BOTH	0x00
+#define LED_RED		0x10
+#define LED_GREEN	0x20
+#define LED_NONE	0x30
+
+#define CDM_CE		0x214
+
+#define TMR_GREEN	0x620
+#define TMR_RED		0x630
+#define TMR_LED_ON	0x24
+#define TMR_LED_OFF	0x34
+
+#define TMR0_ENABLE	0x600
+#define TMR0_INPUT	0x604
+#define TMR0_STATUS	0x60c
+
+
+// ----------------------------------------------------------------------
+// QT sleep
+
+// XXX it uses cca. 10 mA less if registers are saved in .text. WTF
+//	.data
+registers:
+	.space 0x180
+//	.text
+
+// probably not used... just in case...
+#define SPRN_IABR2 1018
+#define SPRN_DABR2 317
+#define SPRN_DCMP 977
+#define SPRN_MBAR 311
+#define SPRN_IBCR 309
+#define SPRN_DBCR 310
+
+// helpers... beware: r10 and r4
+#define SAVE_SPRN(reg, addr)		\
+	mfspr	r10, SPRN_##reg;	\
+	stw	r10, (addr*4)(r4);
+
+#define LOAD_SPRN(reg, addr)		\
+	lwz	r10, (addr*4)(r4);	\
+	mtspr	SPRN_##reg, r10;	\
+	sync;				\
+	isync;
+
+#define debug(x)		\
+	li	r9, x;		\
+	stb	r9, 0x8(r8);	\
+	dcbf	0, r8;
+
+	.globl l5200_qt_sleep
+l5200_qt_sleep:
+
+	// need to accept that irq, so it stays in deep sleep
+	mfmsr	r3
+	ori	r3, r3, 0x8000
+	sync; isync;
+	mtmsr	r3
+	sync; isync
+
+
+	lis	r3, 0xc000
+	mr	r8, r3
+	lis	r4, l5200_wakeup@h
+	ori	r4, r4, l5200_wakeup@l
+	xoris	r4, r4, 0xc000	// & ~0xc000 0000
+	stw	r4, 0(r3)
+
+	debug(1)
+
+	lis	r4, registers@h
+	ori	r4, r4, registers@l
+	// save 0xf0 (0xe0->0x100 gets overwritten when BDI connected;
+	//   even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?))
+	lwz	r10, 0xf0(r3)
+	stw	r10, (0x5f*4)(r4)
+	// save registers to r4 [destroys r10]
+	SAVE_SPRN(LR, 0x3c)
+	bl	save_regs
+
+	debug(2)
+	// flush caches [destroys r3, r4]
+	bl	flush_data_cache
+
+	debug(3)
+
+
+	// put it to sleep
+	mfmsr	r8
+	oris	r8, r8, 0x0004
+	xoris	r8, r8, 0x0004	// POW = 0
+	sync; isync;
+	mtmsr	r8
+	sync; isync;
+
+	mfspr	r8, SPRN_HID0
+	oris	r8, r8, 0x00f0
+	xoris	r8, r8, 0x00d0	// disable all power modes but sleep
+	sync; isync;
+	mtspr	SPRN_HID0, r8
+	sync; isync;
+
+	// turn off with QT chip
+	lis	r3, MBAR@h
+	li	r4, 0x02
+	stb	r4, GPIOW_GPIOE(r3)	// enable gpio_wkup1
+	sync
+
+	stb	r4, GPIOW_DVO(r3)	// "output" high
+	sync
+	stb	r4, GPIOW_DDR(r3)	// output
+	sync
+	stb	r4, GPIOW_DVO(r3)	// output high
+	sync
+
+	// delay
+	// 2000 cycles is cca 12 uS, 10uS should be enough
+	li	r4, 2000
+	mtctr	r4
+1:
+	bdnz-	1b
+
+	// turn off
+	li	r4, 0
+	stb	r4, GPIOW_DVO(r3)	// output low
+	sync
+
+
+	// deep sleep - this puts ram into self-refresh, just moment before
+	//   QT turns us off
+	mfmsr	r8
+	oris	r8, r8, 0x0004	// POW = 1
+	sync; isync;
+	mtmsr	r8
+	sync; isync;
+
+1:
+	b	1b
+
+
+
+// uboot jumps here on resume
+l5200_wakeup:
+	lis	r8, 0x0000
+
+	debug(0x18)
+
+	lis	r3, 0xf000
+	ori	r3, r3, 0x0b00
+	li	r4, 0x30
+	stb	r4, 0x04(r3)
+	sync
+	stb	r4, 0x0c(r3)
+	sync
+	//li	r4, 0x10 // red
+	//li	r4, 0x00 // both
+	//stb	r4, 0x10(r3)
+
+	debug(0x19)
+
+	bl	restore_regs
+
+	debug(0x1a)
+
+	// HIDs, MSR
+	LOAD_SPRN(HID1, 0x39)
+	LOAD_SPRN(HID2, 0x3a)
+
+	debug(0x1b)
+
+	// address translation is tricky (see turn_on_mmu)
+	mfmsr	r10
+	ori	r10, r10, MSR_DR | MSR_IR
+
+	debug(0x1c)
+
+	mtspr	SPRN_SRR1, r10
+	lis	r10, mmu_on@h
+	ori	r10, r10, mmu_on@l
+	mtspr	SPRN_SRR0, r10
+	sync
+	rfi
+mmu_on:
+	// kernel offset (r4 is still set from restore_registers)
+	oris	r4, r4, 0xc000
+	lis	r8, 0xc000
+
+	debug(0x1d)
+
+	// restore MSR
+	lwz	r10, (4*0x3b)(r4)
+	mtmsr	r10
+	sync
+	isync
+
+	debug(0x20)
+
+	mfspr	r10, SPRN_HID0
+	ori	r5, r10, 0x0c00
+	mtspr	SPRN_HID0, r5	// invalidate caches
+	sync
+	isync
+	mtspr	SPRN_HID0, r10
+	sync
+	isync
+
+	debug(0x21)
+
+	lwz	r10, (4*0x38)(r4)
+	mtspr	SPRN_HID0, r10	// restore (enable caches, DPM)
+	// ^ this has to be after address translation set in MSR
+	sync
+	isync
+
+	debug(0x22)
+
+	// r8 is 0xc0000000, from debug
+	// restore 0xf0
+	lwz	r10, (0x5f*4)(r4)
+	stw	r10, 0xf0(r8)
+
+	LOAD_SPRN(LR, 0x3c)
+
+	debug(0x23)
+
+	blr
+
+
+// ----------------------------------------------------------------------
+// boring code: helpers
+
+save_regs:
+	// save registers
+	stw	r0, 0(r4)
+	stw	r1, 0x4(r4)
+	stw	r2, 0x8(r4)
+	stw	r11, 0xc(r4)
+	stw	r12, 0x10(r4)
+	stw	r13, 0x14(r4)
+	stw	r14, 0x18(r4)
+	stw	r15, 0x1c(r4)
+	stw	r16, 0x20(r4)
+	stw	r17, 0x24(r4)
+	stw	r18, 0x28(r4)
+	stw	r19, 0x2c(r4)
+	stw	r20, 0x30(r4)
+	stw	r21, 0x34(r4)
+	stw	r22, 0x38(r4)
+	stw	r23, 0x3c(r4)
+	stw	r24, 0x40(r4)
+	stw	r25, 0x44(r4)
+	stw	r26, 0x48(r4)
+	stw	r27, 0x4c(r4)
+	stw	r28, 0x50(r4)
+	stw	r29, 0x54(r4)
+	stw	r30, 0x58(r4)
+	stw	r31, 0x5c(r4)
+
+	// save MMU regs
+	SAVE_SPRN(DBAT0L, 0x18)
+	SAVE_SPRN(DBAT0U, 0x19)
+	SAVE_SPRN(IBAT0L, 0x1a)
+	SAVE_SPRN(IBAT0U, 0x1b)
+	SAVE_SPRN(DBAT1L, 0x1c)
+	SAVE_SPRN(DBAT1U, 0x1d)
+	SAVE_SPRN(IBAT1L, 0x1e)
+	SAVE_SPRN(IBAT1U, 0x1f)
+	SAVE_SPRN(DBAT2L, 0x20)
+	SAVE_SPRN(DBAT2U, 0x21)
+	SAVE_SPRN(IBAT2L, 0x22)
+	SAVE_SPRN(IBAT2U, 0x23)
+	SAVE_SPRN(DBAT3L, 0x24)
+	SAVE_SPRN(DBAT3U, 0x25)
+	SAVE_SPRN(IBAT3L, 0x26)
+	SAVE_SPRN(IBAT3U, 0x27)
+	SAVE_SPRN(DBAT4L, 0x28)
+	SAVE_SPRN(DBAT4U, 0x29)
+	SAVE_SPRN(IBAT4L, 0x2a)
+	SAVE_SPRN(IBAT4U, 0x2b)
+	SAVE_SPRN(DBAT5L, 0x2c)
+	SAVE_SPRN(DBAT5U, 0x2d)
+	SAVE_SPRN(IBAT5L, 0x2e)
+	SAVE_SPRN(IBAT5U, 0x2f)
+	SAVE_SPRN(DBAT6L, 0x30)
+	SAVE_SPRN(DBAT6U, 0x31)
+	SAVE_SPRN(IBAT6L, 0x32)
+	SAVE_SPRN(IBAT6U, 0x33)
+	SAVE_SPRN(DBAT7L, 0x34)
+	SAVE_SPRN(DBAT7U, 0x35)
+	SAVE_SPRN(IBAT7L, 0x36)
+	SAVE_SPRN(IBAT7U, 0x37)
+
+	SAVE_SPRN(HID0, 0x38)
+	SAVE_SPRN(HID1, 0x39)
+	SAVE_SPRN(HID2, 0x3a)
+
+	mfmsr	r10
+	stw	r10, (4*0x3b)(r4)
+
+	//SAVE_SPRN(LR, 0x3c) have to save it before the call
+
+	SAVE_SPRN(SPRG0, 0x40)
+	SAVE_SPRN(SPRG1, 0x41)
+	SAVE_SPRN(SPRG2, 0x42)
+	SAVE_SPRN(SPRG3, 0x43)
+	SAVE_SPRN(SPRG4, 0x44)
+	SAVE_SPRN(SPRG5, 0x45)
+	SAVE_SPRN(SPRG6, 0x46)
+	SAVE_SPRN(SPRG7, 0x47)
+	SAVE_SPRN(IABR,  0x48)
+	SAVE_SPRN(IABR2, 0x49)
+	SAVE_SPRN(DABR,  0x4a)
+	SAVE_SPRN(DABR2, 0x4b)
+	SAVE_SPRN(DMISS, 0x4c)
+	SAVE_SPRN(DCMP,  0x4d)
+	SAVE_SPRN(IMISS, 0x4e)
+	SAVE_SPRN(ICMP,  0x4f)
+	SAVE_SPRN(HASH1, 0x50)
+	SAVE_SPRN(HASH2, 0x51)
+	SAVE_SPRN(MBAR,  0x52)
+
+	SAVE_SPRN(CTR,   0x54)
+	SAVE_SPRN(DSISR, 0x55)
+	SAVE_SPRN(DAR,   0x56)
+	SAVE_SPRN(RPA,   0x57)
+	SAVE_SPRN(SDR1,  0x58)
+	SAVE_SPRN(DEC,   0x59)
+	SAVE_SPRN(IBCR,  0x5a)
+	SAVE_SPRN(DBCR,  0x5b)
+	SAVE_SPRN(TBRL,  0x5c)
+	SAVE_SPRN(TBRU,  0x5d)
+	// 0x5f reserved by 0xf0
+	blr
+
+// restore registers
+restore_regs:
+	lis	r4, registers@h
+	ori	r4, r4, registers@l
+	xoris	r4, r4, 0xc000	// & ~0xc000 0000
+
+	lwz	r0, 0(r4)
+	lwz	r1, 0x4(r4)
+	lwz	r2, 0x8(r4)
+	lwz	r11, 0xc(r4)
+	lwz	r12, 0x10(r4)
+	lwz	r13, 0x14(r4)
+	lwz	r14, 0x18(r4)
+	lwz	r15, 0x1c(r4)
+	lwz	r16, 0x20(r4)
+	lwz	r17, 0x24(r4)
+	lwz	r18, 0x28(r4)
+	lwz	r19, 0x2c(r4)
+	lwz	r20, 0x30(r4)
+	lwz	r21, 0x34(r4)
+	lwz	r22, 0x38(r4)
+	lwz	r23, 0x3c(r4)
+	lwz	r24, 0x40(r4)
+	lwz	r25, 0x44(r4)
+	lwz	r26, 0x48(r4)
+	lwz	r27, 0x4c(r4)
+	lwz	r28, 0x50(r4)
+	lwz	r29, 0x54(r4)
+	lwz	r30, 0x58(r4)
+	lwz	r31, 0x5c(r4)
+
+	// restore MMU regs
+	LOAD_SPRN(DBAT0L, 0x18)
+	LOAD_SPRN(DBAT0U, 0x19)
+	LOAD_SPRN(IBAT0L, 0x1a)
+	LOAD_SPRN(IBAT0U, 0x1b)
+	LOAD_SPRN(DBAT1L, 0x1c)
+	LOAD_SPRN(DBAT1U, 0x1d)
+	LOAD_SPRN(IBAT1L, 0x1e)
+	LOAD_SPRN(IBAT1U, 0x1f)
+	LOAD_SPRN(DBAT2L, 0x20)
+	LOAD_SPRN(DBAT2U, 0x21)
+	LOAD_SPRN(IBAT2L, 0x22)
+	LOAD_SPRN(IBAT2U, 0x23)
+	LOAD_SPRN(DBAT3L, 0x24)
+	LOAD_SPRN(DBAT3U, 0x25)
+	LOAD_SPRN(IBAT3L, 0x26)
+	LOAD_SPRN(IBAT3U, 0x27)
+	LOAD_SPRN(DBAT4L, 0x28)
+	LOAD_SPRN(DBAT4U, 0x29)
+	LOAD_SPRN(IBAT4L, 0x2a)
+	LOAD_SPRN(IBAT4U, 0x2b)
+	LOAD_SPRN(DBAT5L, 0x2c)
+	LOAD_SPRN(DBAT5U, 0x2d)
+	LOAD_SPRN(IBAT5L, 0x2e)
+	LOAD_SPRN(IBAT5U, 0x2f)
+	LOAD_SPRN(DBAT6L, 0x30)
+	LOAD_SPRN(DBAT6U, 0x31)
+	LOAD_SPRN(IBAT6L, 0x32)
+	LOAD_SPRN(IBAT6U, 0x33)
+	LOAD_SPRN(DBAT7L, 0x34)
+	LOAD_SPRN(DBAT7U, 0x35)
+	LOAD_SPRN(IBAT7L, 0x36)
+	LOAD_SPRN(IBAT7U, 0x37)
+
+	/* these are a bit tricky */
+	/*
+	0x38 - HID0
+	0x39 - HID1
+	0x3a - HID2
+	0x3b - MSR
+	0x3c - LR
+	*/
+
+	// rest of regs
+	LOAD_SPRN(SPRG0, 0x40);
+	LOAD_SPRN(SPRG1, 0x41);
+	LOAD_SPRN(SPRG2, 0x42);
+	LOAD_SPRN(SPRG3, 0x43);
+	LOAD_SPRN(SPRG4, 0x44);
+	LOAD_SPRN(SPRG5, 0x45);
+	LOAD_SPRN(SPRG6, 0x46);
+	LOAD_SPRN(SPRG7, 0x47);
+	LOAD_SPRN(IABR,  0x48);
+	LOAD_SPRN(IABR2, 0x49);
+	LOAD_SPRN(DABR,  0x4a);
+	LOAD_SPRN(DABR2, 0x4b);
+	LOAD_SPRN(DMISS, 0x4c);
+	LOAD_SPRN(DCMP,  0x4d);
+	LOAD_SPRN(IMISS, 0x4e);
+	LOAD_SPRN(ICMP,  0x4f);
+	LOAD_SPRN(HASH1, 0x50);
+	LOAD_SPRN(HASH2, 0x51);
+	LOAD_SPRN(MBAR,  0x52);
+
+	LOAD_SPRN(CTR,   0x54);
+	LOAD_SPRN(DSISR, 0x55);
+	LOAD_SPRN(DAR,   0x56);
+	LOAD_SPRN(RPA,   0x57);
+	LOAD_SPRN(SDR1,  0x58);
+	LOAD_SPRN(DEC,   0x59);
+	LOAD_SPRN(IBCR,  0x5a);
+	LOAD_SPRN(DBCR,  0x5b);
+	LOAD_SPRN(TBWL,  0x5c);	// these two have separate R/W regs
+	LOAD_SPRN(TBWU,  0x5d);
+	// 0x5f reserved by 0xf0
+	blr
+
+
+
+#if 1
+/* cache flushing code. copied from boot/util.S. how do you link with that?! */
+#define NUM_CACHE_LINES 128*8
+#define cache_flush_buffer 0xc0000000
+
+/*
+ * Flush data cache
+ * Do this by just reading lots of stuff into the cache.
+ */
+        .globl flush_data_cache
+flush_data_cache:
+	lis	r3,cache_flush_buffer@h
+	ori	r3,r3,cache_flush_buffer@l
+	li	r4,NUM_CACHE_LINES
+	mtctr	r4
+1:
+	lwz	r4,0(r3)
+	addi	r3,r3,L1_CACHE_BYTES	/* Next line, please */
+	bdnz	1b
+	blr
+#endif
Index: work-powerpc.git/arch/ppc/syslib/mpc52xx_pic.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/mpc52xx_pic.c
+++ work-powerpc.git/arch/ppc/syslib/mpc52xx_pic.c
@@ -301,6 +301,7 @@ mpc52xx_get_irq(void)
 		irq = (status >> 16) & 0x1f;
 		if (irq == 4)			/* low priority peripheral */
 			goto peripheral;
+		printk(KERN_ALERT "%s: %i status: %x\n", __func__, __LINE__, status);
 		irq += MPC52xx_MAIN_IRQ_BASE;
 	}
 	else if (status & 0x20000000) {		/* peripheral */
@@ -317,3 +318,47 @@ peripheral:
 	return irq;
 }
 
+/* save and restore registers for suspend to ram */
+u32 pic_regs[0x10];
+void mpc52xx_pic_suspend(void)
+{
+	int i = 0;
+	// sdma into sdma suspend/resume
+	pic_regs[i++] = in_be32(&sdma->IntPend);
+	pic_regs[i++] = in_be32(&sdma->IntMask);
+	pic_regs[i++] = in_be32(&intr->per_mask);
+	pic_regs[i++] = in_be32(&intr->main_mask);
+	pic_regs[i++] = in_be32(&intr->ctrl);
+	pic_regs[i++] = in_be32(&intr->per_pri1);
+	pic_regs[i++] = in_be32(&intr->per_pri2);
+	pic_regs[i++] = in_be32(&intr->per_pri3);
+	pic_regs[i++] = in_be32(&intr->main_pri1);
+	pic_regs[i++] = in_be32(&intr->main_pri2);
+
+	iounmap(intr);
+	iounmap(sdma);
+	iounmap(slt0);
+}
+
+void mpc52xx_pic_resume(void)
+{
+	int i = 0;
+
+	intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
+	sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
+	slt0 = ioremap(MPC52xx_PA(MPC52xx_SLTx_OFFSET(0)), MPC52xx_SLT_SIZE);
+	if ((intr==NULL) || (sdma==NULL) || (slt0==NULL))
+		panic("Can't ioremap PIC/SDMA/SLT0 registers for init_irq !");
+
+	// sdma into sdma suspend/resume
+	out_be32(&sdma->IntPend, pic_regs[i++]);
+	out_be32(&sdma->IntMask, pic_regs[i++]);
+	out_be32(&intr->per_mask, pic_regs[i++]);
+	out_be32(&intr->main_mask, pic_regs[i++]);
+	out_be32(&intr->ctrl, pic_regs[i++]);
+	out_be32(&intr->per_pri1, pic_regs[i++]);
+	out_be32(&intr->per_pri2, pic_regs[i++]);
+	out_be32(&intr->per_pri3, pic_regs[i++]);
+	out_be32(&intr->main_pri1, pic_regs[i++]);
+	out_be32(&intr->main_pri2, pic_regs[i++]);
+}
Index: work-powerpc.git/drivers/serial/mpc52xx_uart.c
===================================================================
--- work-powerpc.git.orig/drivers/serial/mpc52xx_uart.c
+++ work-powerpc.git/drivers/serial/mpc52xx_uart.c
@@ -285,7 +285,7 @@ mpc52xx_uart_set_termios(struct uart_por
 
 	/* Do our best to flush TX & RX, so we don't loose anything */
 	/* But we don't wait indefinitly ! */
-	j = 5000000;	/* Maximum wait */
+	j = 500000;	/* Maximum wait */
 	/* FIXME Can't receive chars since set_termios might be called at early
 	 * boot for the console, all stuff is not yet ready to receive at that
 	 * time and that just makes the kernel oops */
@@ -771,7 +771,7 @@ mpc52xx_uart_suspend(struct platform_dev
 {
 	struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev);
 
-	if (sport)
+	if (port)
 		uart_suspend_port(&mpc52xx_uart_driver, port);
 
 	return 0;
Index: work-powerpc.git/include/asm-ppc/mpc52xx.h
===================================================================
--- work-powerpc.git.orig/include/asm-ppc/mpc52xx.h
+++ work-powerpc.git/include/asm-ppc/mpc52xx.h
@@ -142,6 +142,12 @@ enum ppc_sys_devices {
 #define MPC52xx_IRQ1			(MPC52xx_MAIN_IRQ_BASE + 1)
 #define MPC52xx_IRQ2			(MPC52xx_MAIN_IRQ_BASE + 2)
 #define MPC52xx_IRQ3			(MPC52xx_MAIN_IRQ_BASE + 3)
+/* see mpc5200b users manual, page 162 (7.2.4.11), and just count... */
+#define MPC52xx_LOINT_IRQ		(MPC52xx_MAIN_IRQ_BASE + 4)
+#define MPC52xx_RTC_PER_IRQ		(MPC52xx_MAIN_IRQ_BASE + 5)
+#define MPC52xx_RTC_STOP_IRQ		(MPC52xx_MAIN_IRQ_BASE + 6)
+#define MPC52xx_GPIO_IRQ		(MPC52xx_MAIN_IRQ_BASE + 7)
+#define MPC52xx_GPIO_WKUP_IRQ		(MPC52xx_MAIN_IRQ_BASE + 8)	/* == 13 */
 
 #define MPC52xx_SDMA_IRQ		(MPC52xx_PERP_IRQ_BASE + 0)
 #define MPC52xx_PSC1_IRQ		(MPC52xx_PERP_IRQ_BASE + 1)
Index: work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
===================================================================
--- work-powerpc.git.orig/drivers/net/fec_mpc52xx/fec.c
+++ work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
@@ -733,14 +733,64 @@ mpc52xx_fec_remove(struct device *dev)
 	return 0;
 }
 
+
+static struct device_driver mpc52xx_fec_driver;
+extern int mpc52xx_fec_mii_suspend(struct net_device *dev);
+int mpc52xx_fec_suspend(struct device *ddev, pm_message_t state)
+{
+#if 1
+	struct net_device *dev = dev_get_drvdata(ddev);
+	struct fec_priv *priv = dev->priv;
+	struct mpc52xx_fec *fec = priv->fec;
+
+	netif_stop_queue(dev);
+	out_be32(&fec->imask, 0x0);
+
+	/* Disable the rx and tx tasks. */
+	sdma_disable(priv->rx_sdma);
+	sdma_disable(priv->tx_sdma);
+
+	/* Stop FEC */
+	out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~0x2);
+
+	mpc52xx_fec_mii_suspend(dev);
+#else
+//	mpc52xx_fec_remove(ddev);
+#endif
+	return 0;
+}
+
+int mpc52xx_fec_resume(struct device *ddev)
+{
+#if 1
+	struct net_device *dev = dev_get_drvdata(ddev);
+	struct fec_priv *priv = dev->priv;
+	//struct mpc52xx_fec *fec = priv->fec;
+
+	/* Restart the DMA tasks */
+	sdma_fec_rx_init(priv->rx_sdma, priv->rx_fifo, FEC_RX_BUFFER_SIZE);
+	sdma_fec_tx_init(priv->tx_sdma, priv->tx_fifo);
+	fec_hw_init(dev);
+	fec_mii_init(dev);
+
+	if (priv->sequence_done) {		 /* redo the fec_open() */
+		fec_free_rx_buffers(priv->rx_sdma);
+		fec_open(dev);
+	}
+#else
+//	mpc52xx_fec_probe(ddev);
+#endif
+	return 0;
+}
+
 static struct device_driver mpc52xx_fec_driver = {
 	.name	  = DRIVER_NAME,
 	.bus		= &platform_bus_type,
 	.probe		= mpc52xx_fec_probe,
 	.remove		= mpc52xx_fec_remove,
 #ifdef CONFIG_PM
-/*	.suspend	= mpc52xx_fec_suspend,	TODO */
-/*	.resume		= mpc52xx_fec_resume,	TODO */
+	.suspend	= mpc52xx_fec_suspend,
+	.resume		= mpc52xx_fec_resume,
 #endif
 };
 
Index: work-powerpc.git/drivers/net/fec_mpc52xx/fec_phy.c
===================================================================
--- work-powerpc.git.orig/drivers/net/fec_mpc52xx/fec_phy.c
+++ work-powerpc.git/drivers/net/fec_mpc52xx/fec_phy.c
@@ -521,6 +521,23 @@ int fec_mii_wait(struct net_device *dev)
 	return 0;
 }
 
+
+phy_cmd_t phy_cmd_powerdown[] =  { { mk_mii_write(0x00, 0x0800), NULL }, // power down
+				{ mk_mii_end, } };
+phy_cmd_t phy_cmd_sleep[] =  { { mk_mii_write(MII_LXT971_PCR, 0x00dc), NULL }, // sleep after 1 sec
+				{ mk_mii_write(0x14, 0xbbb2), NULL }, // disable leds
+				{ mk_mii_end, } };
+phy_cmd_t phy_cmd_wakeup[] =  { { mk_mii_write(MII_LXT971_PCR, 0x0084), NULL },
+				{ mk_mii_end, } };
+phy_cmd_t phy_cmd_poweron[] =  { { mk_mii_write(MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)), NULL },
+	  			{ mk_mii_end, } };
+
+void mpc52xx_fec_mii_suspend(struct net_device *dev)
+{
+	mii_do_cmd(dev, phy_cmd_powerdown);
+	printk(KERN_ALERT "%s: %i\n", __func__, __LINE__);
+}
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Dale Farnsworth");
 MODULE_DESCRIPTION("PHY driver for Motorola MPC52xx FEC");
Index: work-powerpc.git/kernel/power/main.c
===================================================================
--- work-powerpc.git.orig/kernel/power/main.c
+++ work-powerpc.git/kernel/power/main.c
@@ -115,9 +115,26 @@ int suspend_enter(suspend_state_t state)
 		goto Done;
 	}
 	error = pm_ops->enter(state);
+	// zakaj za vraga se ne vrne sem? nima smisla, saj so vsi registri prav nastavljeni
+	*((volatile char *)0xc0000008) = 0x30;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
+
+	*((volatile long *)0xf0000620)   = 0x24;	// timer2 led, green
+	__asm__ __volatile__ ("sync");
+
+	*((volatile long *)0xf0000b10)   = 0x20000000;	// green
+	__asm__ __volatile__ ("sync");
+	*((volatile char *)0xc0000008)   = 0x31;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	device_power_up();
  Done:
+	*((volatile long *)0xf0000b10)   = 0x00000000;	// both
+	*((volatile char *)0xc0000008)   = 0x32;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
+	__asm__ __volatile__ ("sync");
 	local_irq_restore(flags);
+	*((volatile char *)0xc0000008)   = 0x33;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	return error;
 }
 
@@ -132,13 +149,27 @@ int suspend_enter(suspend_state_t state)
 
 static void suspend_finish(suspend_state_t state)
 {
+	*((volatile char *)0xc0000008)   = 0x40;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	device_resume();
+	*((volatile char *)0xc0000008)   = 0x41;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	resume_console();
+	*((volatile char *)0xc0000008)   = 0x42;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	thaw_processes();
+	*((volatile char *)0xc0000008)   = 0x43;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	enable_nonboot_cpus();
+	*((volatile char *)0xc0000008)   = 0x44;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	if (pm_ops && pm_ops->finish)
 		pm_ops->finish(state);
+	*((volatile char *)0xc0000008)   = 0x45;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	pm_restore_console();
+	*((volatile char *)0xc0000008)   = 0x46;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 }
 
 
@@ -197,10 +228,17 @@ static int enter_state(suspend_state_t s
 	pr_debug("PM: Entering %s sleep\n", pm_states[state]);
 	error = suspend_enter(state);
 
+	*((volatile char *)0xc0000008)   = 0x35;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	pr_debug("PM: Finishing wakeup.\n");
+	*((volatile char *)0xc0000008)   = 0x36;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
 	suspend_finish(state);
+	*((volatile char *)0xc0000008)   = 0x37;
+	asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
  Unlock:
 	up(&pm_sem);
+	*((volatile char *)0xc0000008)   = 0x38;
 	return error;
 }
 
Index: work-powerpc.git/drivers/base/power/resume.c
===================================================================
--- work-powerpc.git.orig/drivers/base/power/resume.c
+++ work-powerpc.git/drivers/base/power/resume.c
@@ -70,10 +70,14 @@ static int resume_device_early(struct de
 void dpm_resume(void)
 {
 	down(&dpm_list_sem);
+	int foo = 0x50;
 	while(!list_empty(&dpm_off)) {
 		struct list_head * entry = dpm_off.next;
 		struct device * dev = to_device(entry);
 
+		*((volatile char *)0xc0000008)   = foo++;
+		asm volatile("dcbf 0,%0" : : "r" (0xc0000008) : "memory");
+
 		get_device(dev);
 		list_move_tail(entry, &dpm_active);
 
Index: work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/mpc52xx_setup.c
+++ work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
@@ -86,8 +86,11 @@ mpc52xx_map_io(void)
 	/* Here we map the MBAR and the whole upper zone. MBAR is only
 	   64k but we can't map only 64k with BATs. Map the whole
 	   0xf0000000 range is ok and helps eventual lpb devices placed there */
+	/* Disable guarded bit, so we can execute code from SRAM.
+	 * Unfortunately, mappings are >= 2**17, so we "unguard" MBAR registers
+	 * too. This probably isn't a problem. */
 	io_block_mapping(
-		MPC52xx_MBAR_VIRT, MPC52xx_MBAR, 0x10000000, _PAGE_IO);
+		MPC52xx_MBAR_VIRT, MPC52xx_MBAR, 0x10000000, _PAGE_IO & ~_PAGE_GUARDED);
 }
 
 
@@ -217,8 +220,7 @@ mpc52xx_calibrate_decr(void)
 	tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
 }
 
-void __init
-mpc52xx_setup_cpu(void)
+void mpc52xx_setup_cpu(void)
 {
 	struct mpc52xx_cdm  __iomem *cdm;
 	struct mpc52xx_xlb  __iomem *xlb;
Index: work-powerpc.git/arch/ppc/platforms/lite5200.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/lite5200.c
+++ work-powerpc.git/arch/ppc/platforms/lite5200.c
@@ -93,8 +93,7 @@ lite5200_map_irq(struct pci_dev *dev, un
 #endif
 #endif
 
-static void __init
-lite5200_setup_cpu(void)
+void lite5200_setup_cpu(void)
 {
 	struct mpc52xx_gpio __iomem *gpio;
 	struct mpc52xx_intr __iomem *intr;
@@ -149,8 +148,7 @@ unmap_regs:
 	if (intr) iounmap(intr);
 }
 
-static void __init
-lite5200_setup_arch(void)
+static void lite5200_setup_arch(void)
 {
 	/* CPU & Port mux setup */
 	mpc52xx_setup_cpu();	/* Generic */
Index: work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/bestcomm/bestcomm.c
+++ work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
@@ -361,6 +361,77 @@ out:
 }
 
 
+/* saved sdma regs */
+struct mpc52xx_sdma sdma_regs;
+char saved_sram[0x4000]; // 16 kB
+static int mpc52xx_sdma_suspend(struct device *dev, pm_message_t state)
+{
+	int i;
+
+	/* don't actually need the be variants */
+	sdma_regs.taskBar =	in_be32(&sdma.io->taskBar);
+	sdma_regs.currentPointer = in_be32(&sdma.io->currentPointer);
+	sdma_regs.endPointer =	in_be32(&sdma.io->endPointer);
+	sdma_regs.variablePointer = in_be32(&sdma.io->variablePointer);
+	sdma_regs.IntVect1 =	in_8(&sdma.io->IntVect1);
+	sdma_regs.IntVect2 =	in_8(&sdma.io->IntVect2);
+	sdma_regs.PtdCntrl =	in_be16(&sdma.io->PtdCntrl);
+	sdma_regs.IntPend =	in_be32(&sdma.io->IntPend);
+	sdma_regs.IntMask =	in_be32(&sdma.io->IntMask);
+
+	for (i=0; i<16; i++)
+		sdma_regs.tcr[i] = in_be16(&sdma.io->tcr[i]);
+	for (i=0; i<32; i++)
+		sdma_regs.ipr[i] = in_8(&sdma.io->ipr[i]);
+
+	sdma_regs.cReqSelect =	in_be32(&sdma.io->cReqSelect);
+	sdma_regs.task_size[0] = in_be32(&sdma.io->task_size[0]);
+	sdma_regs.task_size[1] = in_be32(&sdma.io->task_size[1]);
+	sdma_regs.MDEDebug =	in_be32(&sdma.io->MDEDebug);
+	sdma_regs.Value1 =	in_be32(&sdma.io->Value1);
+	sdma_regs.Value2 =	in_be32(&sdma.io->Value2);
+	sdma_regs.Control =	in_be32(&sdma.io->Control);
+	sdma_regs.Status =	in_be32(&sdma.io->Status);
+	sdma_regs.PTDDebug =	in_be32(&sdma.io->PTDDebug);
+
+	memcpy(saved_sram, sdma.sram, sdma.sram_size);
+	return 0;
+}
+
+static int mpc52xx_sdma_resume(struct device *dev)
+{
+	int i;
+
+	memcpy(sdma.sram, saved_sram, sdma.sram_size);
+
+	// XXX order?
+	out_be32(&sdma.io->taskBar, sdma_regs.taskBar);
+	out_be32(&sdma.io->currentPointer, sdma_regs.currentPointer);
+	out_be32(&sdma.io->endPointer, sdma_regs.endPointer);
+	out_be32(&sdma.io->variablePointer, sdma_regs.variablePointer);
+	out_8(&sdma.io->IntVect1, sdma_regs.IntVect1);
+	out_8(&sdma.io->IntVect2, sdma_regs.IntVect2);
+	out_be16(&sdma.io->PtdCntrl, sdma_regs.PtdCntrl);
+	out_be32(&sdma.io->IntPend, sdma_regs.IntPend);
+	out_be32(&sdma.io->IntMask, sdma_regs.IntMask);
+
+	for (i=0; i<16; i++)
+		out_be16(&sdma.io->tcr[i], sdma_regs.tcr[i]);
+	for (i=0; i<32; i++)
+		out_8(&sdma.io->ipr[i], sdma_regs.ipr[i]);
+
+	out_be32(&sdma.io->cReqSelect, sdma_regs.cReqSelect);
+	out_be32(&sdma.io->task_size[0], sdma_regs.task_size[0]);
+	out_be32(&sdma.io->task_size[1], sdma_regs.task_size[1]);
+	out_be32(&sdma.io->MDEDebug, sdma_regs.MDEDebug);
+	out_be32(&sdma.io->Value1, sdma_regs.Value1);
+	out_be32(&sdma.io->Value2, sdma_regs.Value2);
+	out_be32(&sdma.io->Control, sdma_regs.Control);
+	out_be32(&sdma.io->Status, sdma_regs.Status);
+	out_be32(&sdma.io->PTDDebug, sdma_regs.PTDDebug);
+	return 0;
+}
+
 static struct device_driver mpc52xx_sdma_driver = {
 	.owner	  = THIS_MODULE,
 	.name	  = DRIVER_NAME,
@@ -368,8 +439,8 @@ static struct device_driver mpc52xx_sdma
 	.probe 	  = mpc52xx_sdma_probe,
 /*	.remove	  = mpc52xx_sdma_remove,	TODO */
 #ifdef CONFIG_PM
-/*	.suspend	= mpc52xx_sdma_suspend,	TODO */
-/*	.resume		= mpc52xx_sdma_resume,	TODO */
+	.suspend	= mpc52xx_sdma_suspend,
+	.resume		= mpc52xx_sdma_resume,
 #endif
 };
 

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

* Re: [RFC] lite5200b low power mode
  2006-12-22 10:54 [RFC] lite5200b low power mode Domen Puncer
@ 2006-12-22 11:10 ` Sylvain Munaut
  2007-01-23  9:33 ` [RFC PATCHES] lite5200b low power mode works Domen Puncer
  1 sibling, 0 replies; 7+ messages in thread
From: Sylvain Munaut @ 2006-12-22 11:10 UTC (permalink / raw)
  To: Domen Puncer; +Cc: linuxppc-embedded

Domen Puncer wrote:
> Hi!
>
> I have managed to implement low power mode... well, to some
> degree, at least.
>
> Short description:
> patch u-boot, it's the return from wakeup code. pretty clean.
> 	address is saved at 0x0 (0xc000_0000)
> patch linux with PM-platform_bus-and-late_suspend-early_resume.patch
> 	didn't investigate too much, but it seems to broke mine.
> patch linux with lite5200b_low_power.patch
> 	ugly, with debugging, hacks and whatever
>
> Boot Linux and 'echo mem > /sys/power/state'.
> It won't work on PCI, possibly other devices, PSC0 and FEC seem
> to work ok for me.
>
> It seems to work reliably (checksums match, so ram was in self-refresh)
> when called from boot scripts, but not so when from console prompt.
>
>
> If someone's interested (or maybe wants to play with this during
> holiday? ;-) ), more info follow:
>
>   
Well, I'll have a look, that looks interesting ;)


    Sylvain

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

* Re: [RFC PATCHES] lite5200b low power mode works
  2006-12-22 10:54 [RFC] lite5200b low power mode Domen Puncer
  2006-12-22 11:10 ` Sylvain Munaut
@ 2007-01-23  9:33 ` Domen Puncer
  2007-02-19  7:21   ` Domen Puncer
  1 sibling, 1 reply; 7+ messages in thread
From: Domen Puncer @ 2007-01-23  9:33 UTC (permalink / raw)
  To: linuxppc-embedded

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

On 22/12/06 11:54 +0100, Domen Puncer wrote:
> Hi!
> 
> I have managed to implement low power mode... well, to some
> degree, at least.

And now, I can't break it anymore, it works every time on my
setup. :-)

How to use?
Patch u-boot, Linux. Run it. echo mem > /sys/power/state .


Patches in order they apply:
u-boot.patch
	wakeup ram from self-refresh and jump to kernel wakeup code
fec_platform_fix
sdma_platform_fix
	since these are platform drivers (.bus = ...), they need
	to have the proper structs so suspend_late will work
uart_sport_fix
	a typo fix in suspend code
sram_executable
	don't guard SRAM, since we execute some code from it
low_power_mode
	"main" patch, with suspend, self-refresh and resume code
suspend_arch_code_non-init
	call ppc_md.setup_arch() on resume
suspend_pic
	suspend and resume pic
uart_smaller_delay
	because 5 seconds delay is a bit long
suspend_bestcomm
	save/restore SRAM and bestcomm registers
suspend_fec
	fec suspend/resume



Update from previous patch:
the correct sequence for self-refresh seems to be:
mode_en = 1
soft_pre = 1
mode_en = 0, soft_pre = 0
ref_en = 1, cke = 0

Note the "mode_en = 0", I couldn't find it documented anywhere, and
even in freescale's examples it's missing.


I'm planning on porting this to Grant/Sylvain's arch/powerpc mpc52xx
tree soon.

Any comments are very appreaciated.


	Domen

[-- Attachment #2: u-boot.patch --]
[-- Type: text/plain, Size: 5112 bytes --]

diff --git a/board/icecube/icecube.c b/board/icecube/icecube.c
index 4f056b2..3a52557 100644
--- a/board/icecube/icecube.c
+++ b/board/icecube/icecube.c
@@ -38,6 +38,154 @@ # else
 #include "mt48lc16m16a2-75.h"
 # endif
 #endif
+
+#ifdef CONFIG_LITE5200B
+/* u-boot part of low-power mode implementation */
+#define SAVED_ADDR 0x00000000
+#define SDRAM_CONTROLW (SDRAM_CONTROL & ~0x50000000) /* !ref_en !cke */
+
+static void wakeup_sdram()
+{
+	long hi_addr_bit = 0x01000000;
+
+	/* unlock mode register */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000000;
+	__asm__ volatile ("sync");
+
+	/* precharge all banks */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000002;
+	__asm__ volatile ("sync");
+
+#if SDRAM_DDR
+	/* set mode register: extended mode */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_EMODE;
+	__asm__ volatile ("sync");
+
+	/* set mode register: reset DLL */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_MODE | 0x04000000;
+	__asm__ volatile ("sync");
+#endif
+
+	/* precharge all banks */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000002;
+	__asm__ volatile ("sync");
+
+	/* auto refresh */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000004;
+	__asm__ volatile ("sync");
+
+	/* set mode register */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_MODE;
+	__asm__ volatile ("sync");
+
+
+	/* the second bank too */
+	/* unlock mode register */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000000 | hi_addr_bit;
+	__asm__ volatile ("sync");
+
+	/* precharge all banks */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000002 | hi_addr_bit;
+	__asm__ volatile ("sync");
+
+#if SDRAM_DDR
+	/* set mode register: extended mode */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_EMODE;
+	__asm__ volatile ("sync");
+
+	/* set mode register: reset DLL */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_MODE | 0x04000000;
+	__asm__ volatile ("sync");
+#endif
+
+	/* precharge all banks */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000002 | hi_addr_bit;
+	__asm__ volatile ("sync");
+
+	/* auto refresh */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000004 | hi_addr_bit;
+	__asm__ volatile ("sync");
+
+	/* set mode register */
+	*(vu_long *)MPC5XXX_SDRAM_MODE = SDRAM_MODE;
+	__asm__ volatile ("sync");
+
+
+	/* out of self refresh */
+	/* ref_en, cke, out of self refresh */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000000 | 0x50000000;
+	__asm__ volatile ("sync");
+
+	/* normal operation */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x50000000;
+	__asm__ volatile ("sync");
+
+	/* second bank too */
+	/* ref_en, cke, out of self refresh */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x80000000 | 0x50000000 | hi_addr_bit;
+	__asm__ volatile ("sync");
+
+	/* normal operation */
+	*(vu_long *)MPC5XXX_SDRAM_CTRL = SDRAM_CONTROLW | 0x50000000 | hi_addr_bit;
+	__asm__ volatile ("sync");
+}
+
+static void lite5200b_wakeup(void)
+{
+	unsigned char wakeup_pin;
+	void (*linux_wakeup)(void);
+
+	/* check PSC2_4, if it's down "QT" is signaling we have a wakeup
+	 * from low power mode */
+	*(vu_char *)MPC5XXX_WU_GPIO_ENABLE = 0x02;
+	__asm__ volatile ("sync");
+
+	wakeup_pin = *(vu_char *)MPC5XXX_WU_GPIO_DATA_I;
+	if (wakeup_pin & 0x02)
+		return;
+
+	/* acknowledge to "QT"
+	 * by holding pin at 1 for 10 uS */
+	*(vu_char *)MPC5XXX_WU_GPIO_DIR = 0x02;
+	__asm__ volatile ("sync");
+	*(vu_char *)MPC5XXX_WU_GPIO_DATA = 0x02;
+	__asm__ volatile ("sync");
+	udelay(10);
+
+	/* setup SDRAM chip selects */
+	/* 2 128 mb chips */
+	*(vu_long *)MPC5XXX_SDRAM_CS0CFG = 0x0000001a;/* 128mb at 0x0 */
+	*(vu_long *)MPC5XXX_SDRAM_CS1CFG = 0x0800001a;/* 128mb at 0x08000000 */
+	__asm__ volatile ("sync");
+
+	/* setup config registers */
+	*(vu_long *)MPC5XXX_SDRAM_CONFIG1 = SDRAM_CONFIG1;
+	*(vu_long *)MPC5XXX_SDRAM_CONFIG2 = SDRAM_CONFIG2;
+	__asm__ volatile ("sync");
+
+	/* set tap delay */
+	*(vu_long *)MPC5XXX_CDM_PORCFG = SDRAM_TAPDELAY;
+	__asm__ volatile ("sync");
+
+	*(vu_long *)MPC5XXX_SDRAM_SDELAY = 0x04;
+	__asm__ volatile ("sync");
+
+	wakeup_sdram(0);
+	wakeup_sdram(1);
+	udelay(10); /* wait a bit */
+
+	linux_wakeup = *(unsigned long *)SAVED_ADDR;
+
+	/* jump back to linux kernel code */
+	printf("\n\nLooks like we just woke, transferring control to 0x%08lx\n",
+			linux_wakeup);
+	linux_wakeup();
+}
+#else
+#define lite5200b_wakeup
+#endif
+
+
 #ifndef CFG_RAMBOOT
 static void sdram_start (int hi_addr)
 {
@@ -92,6 +240,8 @@ long int initdram (int board_type)
 	ulong dramsize2 = 0;
 	uint svr, pvr;
 
+	lite5200b_wakeup();
+
 #ifndef CFG_RAMBOOT
 	ulong test1, test2;
 
diff --git a/include/mpc5xxx.h b/include/mpc5xxx.h
index 1d20d1d..c7eb090 100644
--- a/include/mpc5xxx.h
+++ b/include/mpc5xxx.h
@@ -189,6 +189,7 @@ #define MPC5XXX_WU_GPIO_ENABLE  (MPC5XXX
 #define MPC5XXX_WU_GPIO_ODE     (MPC5XXX_WU_GPIO + 0x0004)
 #define MPC5XXX_WU_GPIO_DIR     (MPC5XXX_WU_GPIO + 0x0008)
 #define MPC5XXX_WU_GPIO_DATA    (MPC5XXX_WU_GPIO + 0x000c)
+#define MPC5XXX_WU_GPIO_DATA_I  (MPC5XXX_WU_GPIO + 0x0020)
 
 /* PCI registers */
 #define MPC5XXX_PCI_CMD		(MPC5XXX_PCI + 0x04)

[-- Attachment #3: fec_platform_fix --]
[-- Type: text/plain, Size: 2200 bytes --]

Index: work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
===================================================================
--- work-powerpc.git.orig/drivers/net/fec_mpc52xx/fec.c
+++ work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
@@ -553,10 +553,9 @@ static void fec_reinit(struct net_device
 /* ======================================================================== */
 
 static int __devinit
-mpc52xx_fec_probe(struct device *dev)
+mpc52xx_fec_probe(struct platform_device *pdev)
 {
 	int ret;
-	struct platform_device *pdev = to_platform_device(dev);
 	struct net_device *ndev;
 	struct fec_priv *priv = NULL;
 	struct resource *mem;
@@ -680,7 +679,7 @@ mpc52xx_fec_probe(struct device *dev)
 	fec_mii_init(ndev);
 
 	/* We're done ! */
-	dev_set_drvdata(dev, ndev);
+	platform_set_drvdata(pdev, ndev);
 
 	return 0;
 
@@ -707,12 +706,11 @@ probe_error:
 }
 
 static int
-mpc52xx_fec_remove(struct device *dev)
+mpc52xx_fec_remove(struct platform_device *pdev)
 {
-	struct net_device *ndev;
+	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct fec_priv *priv;
 
-	ndev = (struct net_device *) dev_get_drvdata(dev);
 	if (!ndev)
 		return 0;
 	priv = (struct fec_priv *) ndev->priv;
@@ -729,19 +727,20 @@ mpc52xx_fec_remove(struct device *dev)
 
 	free_netdev(ndev);
 
-	dev_set_drvdata(dev, NULL);
+	platform_set_drvdata(pdev, NULL);
 	return 0;
 }
 
-static struct device_driver mpc52xx_fec_driver = {
-	.name	  = DRIVER_NAME,
-	.bus		= &platform_bus_type,
+static struct platform_driver mpc52xx_fec_driver = {
 	.probe		= mpc52xx_fec_probe,
 	.remove		= mpc52xx_fec_remove,
 #ifdef CONFIG_PM
 /*	.suspend	= mpc52xx_fec_suspend,	TODO */
 /*	.resume		= mpc52xx_fec_resume,	TODO */
 #endif
+	.driver	= {
+		.name	  = DRIVER_NAME,
+	},
 };
 
 /* ======================================================================== */
@@ -751,13 +750,13 @@ static struct device_driver mpc52xx_fec_
 static int __init
 mpc52xx_fec_init(void)
 {
-	return driver_register(&mpc52xx_fec_driver);
+	return platform_driver_register(&mpc52xx_fec_driver);
 }
 
 static void __exit
 mpc52xx_fec_exit(void)
 {
-	driver_unregister(&mpc52xx_fec_driver);
+	platform_driver_unregister(&mpc52xx_fec_driver);
 }
 
 

[-- Attachment #4: sdma_platform_fix --]
[-- Type: text/plain, Size: 1426 bytes --]

Index: work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/bestcomm/bestcomm.c
+++ work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
@@ -264,9 +264,8 @@ void sdma_free(struct sdma *s)
 	kfree(s);
 }
 
-static int __devinit mpc52xx_sdma_probe(struct device *dev)
+static int __devinit mpc52xx_sdma_probe(struct platform_device *pdev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
 	int task;
 	u32 *context;
 	u32 *fdt;
@@ -361,30 +360,31 @@ out:
 }
 
 
-static struct device_driver mpc52xx_sdma_driver = {
-	.owner	  = THIS_MODULE,
-	.name	  = DRIVER_NAME,
-	.bus	  = &platform_bus_type,
+static struct platform_driver mpc52xx_sdma_driver = {
 	.probe 	  = mpc52xx_sdma_probe,
 /*	.remove	  = mpc52xx_sdma_remove,	TODO */
 #ifdef CONFIG_PM
 /*	.suspend	= mpc52xx_sdma_suspend,	TODO */
 /*	.resume		= mpc52xx_sdma_resume,	TODO */
 #endif
+	.driver	= {
+		.owner	  = THIS_MODULE,
+		.name	  = DRIVER_NAME,
+	},
 };
 
 static int __init
 mpc52xx_sdma_init(void)
 {
 	printk(KERN_INFO "DMA: MPC52xx BestComm driver\n");
-	return driver_register(&mpc52xx_sdma_driver);
+	return platform_driver_register(&mpc52xx_sdma_driver);
 }
 
 #ifdef MODULE
 static void __exit
 mpc52xx_sdma_exit(void)
 {
-	driver_unregister(&mpc52xx_sdma_driver);
+	platform_driver_unregister(&mpc52xx_sdma_driver);
 }
 #endif
 

[-- Attachment #5: uart_sport_fix --]
[-- Type: text/plain, Size: 459 bytes --]

Index: work-powerpc.git/drivers/serial/mpc52xx_uart.c
===================================================================
--- work-powerpc.git.orig/drivers/serial/mpc52xx_uart.c
+++ work-powerpc.git/drivers/serial/mpc52xx_uart.c
@@ -771,7 +771,7 @@ mpc52xx_uart_suspend(struct platform_dev
 {
 	struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev);
 
-	if (sport)
+	if (port)
 		uart_suspend_port(&mpc52xx_uart_driver, port);
 
 	return 0;

[-- Attachment #6: sram_executable --]
[-- Type: text/plain, Size: 815 bytes --]

Index: work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/mpc52xx_setup.c
+++ work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
@@ -86,8 +86,11 @@ mpc52xx_map_io(void)
 	/* Here we map the MBAR and the whole upper zone. MBAR is only
 	   64k but we can't map only 64k with BATs. Map the whole
 	   0xf0000000 range is ok and helps eventual lpb devices placed there */
+	/* Disable guarded bit, so we can execute code from SRAM.
+	 * Unfortunately, mappings are >= 2**17, so we "unguard" MBAR registers
+	 * too. This probably isn't a problem. */
 	io_block_mapping(
-		MPC52xx_MBAR_VIRT, MPC52xx_MBAR, 0x10000000, _PAGE_IO);
+		MPC52xx_MBAR_VIRT, MPC52xx_MBAR, 0x10000000, _PAGE_IO & ~_PAGE_GUARDED);
 }
 
 

[-- Attachment #7: low_power_mode --]
[-- Type: text/plain, Size: 13371 bytes --]

Index: work-powerpc.git/arch/ppc/platforms/Makefile
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/Makefile
+++ work-powerpc.git/arch/ppc/platforms/Makefile
@@ -28,6 +28,10 @@ obj-$(CONFIG_SANDPOINT)		+= sandpoint.o
 obj-$(CONFIG_SBC82xx)		+= sbc82xx.o
 obj-$(CONFIG_SPRUCE)		+= spruce.o
 obj-$(CONFIG_LITE5200)		+= lite5200.o
+ifeq ($(CONFIG_LITE5200),y)
+obj-$(CONFIG_PM)		+= lite5200_pm.o
+obj-$(CONFIG_PM)		+= lite5200_sleep.o
+endif
 obj-$(CONFIG_MEDIA5200)		+= media5200.o
 obj-$(CONFIG_EV64360)		+= ev64360.o
 obj-$(CONFIG_MPC86XADS)		+= mpc866ads_setup.o
Index: work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
@@ -0,0 +1,72 @@
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/machdep.h>
+
+extern void l5200_qt_sleep(void);
+
+static int l5200_pm_valid(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_MEM:
+	case PM_SUSPEND_STANDBY:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int l5200_pm_prepare(suspend_state_t state)
+{
+	return 0;
+}
+
+static int l5200_pm_enter(suspend_state_t state)
+{
+	struct mpc52xx_gpio __iomem *gpio;
+	u32 gpio_saved_pc;
+
+	/* Map zones */
+	gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE);
+
+	if (!gpio) {
+		printk(KERN_ERR "%s:%i Error mapping io zones\n", __func__, __LINE__);
+		return 1;
+	}
+
+	/* save ie. ethernet pins configuration */
+	gpio_saved_pc = gpio->port_config;
+	iounmap(gpio);
+
+	if (state == PM_SUSPEND_MEM)
+		l5200_qt_sleep();
+
+	gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE);
+	/* restore ie. ethernet pins configuration */
+	gpio->port_config = gpio_saved_pc;
+	iounmap(gpio);
+
+	return 0;
+}
+
+static int l5200_pm_finish(suspend_state_t state)
+{
+	return 0;
+}
+
+static struct pm_ops lite5200_pm_ops = {
+	.valid		= l5200_pm_valid,
+	.prepare	= l5200_pm_prepare,
+	.enter		= l5200_pm_enter,
+	.finish		= l5200_pm_finish,
+};
+
+static int __init lite5200_pm_init(void)
+{
+	pm_set_ops(&lite5200_pm_ops);
+	return 0;
+}
+
+arch_initcall(lite5200_pm_init);
Index: work-powerpc.git/arch/ppc/platforms/lite5200_sleep.S
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/ppc/platforms/lite5200_sleep.S
@@ -0,0 +1,528 @@
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+
+#define MBAR	0xf0000000
+
+#define SDRAM_MODE	0x100
+#define SDRAM_CTRL	0x104
+#define SDRAM_CFG1	0x108
+#define SDRAM_CFG2	0x10c
+#define SC_MODE_EN	(1<<31)
+#define SC_CKE		(1<<30)
+#define SC_REF_EN	(1<<28)
+#define SC_SOFT_PRE	(1<<1)
+
+#define GPIO_CFG	0xb00
+#define GPIO_GPIOE	0xb04
+#define GPIO_ODE	0xb08
+#define GPIO_DDR	0xb0c
+#define GPIO_DVO	0xb10
+
+#define GPIOW_GPIOE	0xc00
+#define GPIOW_ODE	0xc04
+#define GPIOW_DDR	0xc08
+#define GPIOW_DVO	0xc0c
+#define GPIOW_INTEN	0xc10
+
+#define LED_BOTH	0x00
+#define LED_RED		0x10
+#define LED_GREEN	0x20
+#define LED_NONE	0x30
+
+#define CDM_CE		0x214
+#define CDM_SDRAM	(1<<3)
+
+#define TMR_GREEN	0x620
+#define TMR_RED		0x630
+#define TMR_LED_ON	0x24
+#define TMR_LED_OFF	0x34
+
+#define TMR0_ENABLE	0x600
+#define TMR0_INPUT	0x604
+#define TMR0_STATUS	0x60c
+
+#define SRAM		0x8000
+
+// about 2000 cpu cycles for one sdram cycle here
+// just increase, to be on the safe side?
+#define TCK	5000
+
+
+// ----------------------------------------------------------------------
+// QT sleep
+
+// XXX it uses cca. 10 mA less if registers are saved in .text. WTF
+// is this still true?
+//	.data
+registers:
+	.space 0x180
+//	.text
+
+// probably not used... just in case...
+#define SPRN_IABR2 1018
+#define SPRN_DABR2 317
+#define SPRN_DCMP 977
+#define SPRN_MBAR 311
+#define SPRN_IBCR 309
+#define SPRN_DBCR 310
+
+// helpers... beware: r10 and r4
+#define SAVE_SPRN(reg, addr)		\
+	mfspr	r10, SPRN_##reg;	\
+	stw	r10, (addr*4)(r4);
+
+#define LOAD_SPRN(reg, addr)		\
+	lwz	r10, (addr*4)(r4);	\
+	mtspr	SPRN_##reg, r10;	\
+	sync;				\
+	isync;
+
+
+	.globl l5200_qt_sleep
+l5200_qt_sleep:
+
+	// setup wakeup address for u-boot
+	lis	r3, 0xc000
+	lis	r4, l5200_wakeup@h
+	ori	r4, r4, l5200_wakeup@l
+	xoris	r4, r4, 0xc000	// & ~0xc000 0000
+	stw	r4, 0(r3)
+
+
+	// save stuff BDI overwrites
+	/* save 0xf0 (0xe0->0x100 gets overwritten when BDI connected;
+	 *   even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?))
+	 * WARNING: self-refresh doesn't seem to work when BDI2000 is connected,
+	 *   possibly because BDI sets SDRAM registers before wakeup code does */
+	lis	r4, registers@h
+	ori	r4, r4, registers@l
+	lwz	r10, 0xf0(r3)
+	stw	r10, (0x59*4)(r4)
+
+	// save registers to r4 [destroys r10]
+	SAVE_SPRN(LR, 0x3c)
+	bl	save_regs
+
+	// flush caches [destroys r3, r4]
+	bl	flush_data_cache
+
+
+	// copy code to sram
+	lis	r4, MBAR@h
+	ori	r4, r4, SRAM
+	li	r3, (sram_code_end - sram_code)/4
+	mtctr	r3
+	lis	r3, sram_code@h
+	ori	r3, r3, sram_code@l
+1:
+	lwz	r5, 0(r3)
+	stw	r5, 0(r4)
+	addi	r3, r3, 4
+	addi	r4, r4, 4
+	bdnz	1b
+
+	// disable I and D caches
+	mfspr	r3, SPRN_HID0
+	ori	r3, r3, HID0_ICE | HID0_DCE
+	xori	r3, r3, HID0_ICE | HID0_DCE
+	sync; isync;
+	mtspr	SPRN_HID0, r3
+	sync; isync;
+
+	// jump to sram
+	lis	r3, MBAR@h
+	ori	r3, r3, SRAM
+	mtlr	r3
+	blrl
+
+	// doesn't return
+
+
+sram_code:
+	// self refresh
+	lis	r3, MBAR@h
+
+	lwz	r4, SDRAM_CTRL(r3)
+
+	// send NOP (precharge)
+	oris	r4, r4, SC_MODE_EN@h	// mode_en
+	stw	r4, SDRAM_CTRL(r3)
+	sync
+
+	ori	r4, r4, SC_SOFT_PRE	// soft_pre
+	stw	r4, SDRAM_CTRL(r3)
+	sync
+	xori	r4, r4, SC_SOFT_PRE
+
+	xoris	r4, r4, SC_MODE_EN@h	// !mode_en
+	stw	r4, SDRAM_CTRL(r3)
+	sync
+
+	// delay for one sdram cycle (for NOP to finish)
+	li	r5, TCK
+	mtctr	r5
+1:	bdnz-	1b
+
+	// mode_en must not be set when enabling self-refresh
+	// send AR with CKE low (self-refresh)
+	oris	r4, r4, (SC_REF_EN | SC_CKE)@h
+	xoris	r4, r4, (SC_CKE)@h	// ref_en !cke
+	stw	r4, SDRAM_CTRL(r3)
+	sync
+
+	// delay for two sdram cycles (after !CKE there should be two cycles)
+	li	r5, 2*TCK
+	mtctr	r5
+1:	bdnz-	1b
+
+	// disable clock
+	lwz	r4, CDM_CE(r3)
+	ori	r4, r4, CDM_SDRAM
+	xori	r4, r4, CDM_SDRAM
+	stw	r4, CDM_CE(r3)
+	sync
+
+	// delay for two sdram cycles
+	li	r5, 2*TCK
+	mtctr	r5
+1:	bdnz-	1b
+
+
+	// turn off with QT chip
+	lis	r3, MBAR@h
+	li	r4, 0x02
+	stb	r4, GPIOW_GPIOE(r3)	// enable gpio_wkup1
+	sync
+
+	stb	r4, GPIOW_DVO(r3)	// "output" high
+	sync
+	stb	r4, GPIOW_DDR(r3)	// output
+	sync
+	stb	r4, GPIOW_DVO(r3)	// output high
+	sync
+
+	// delay
+	// 2000 cycles is cca 12 uS, 10uS should be enough
+	li	r4, 2000
+	mtctr	r4
+1:
+	bdnz-	1b
+
+	// turn off
+	li	r4, 0
+	stb	r4, GPIOW_DVO(r3)	// output low
+	sync
+
+	// wait until we're offline
+1:
+	b	1b
+sram_code_end:
+
+
+
+// uboot jumps here on resume
+l5200_wakeup:
+	bl	restore_regs
+
+
+	// HIDs, MSR
+	LOAD_SPRN(HID1, 0x39)
+	LOAD_SPRN(HID2, 0x3a)
+
+
+	// address translation is tricky (see turn_on_mmu)
+	mfmsr	r10
+	ori	r10, r10, MSR_DR | MSR_IR
+
+
+	mtspr	SPRN_SRR1, r10
+	lis	r10, mmu_on@h
+	ori	r10, r10, mmu_on@l
+	mtspr	SPRN_SRR0, r10
+	sync
+	rfi
+mmu_on:
+	// kernel offset (r4 is still set from restore_registers)
+	oris	r4, r4, 0xc000
+
+
+	// restore MSR
+	lwz	r10, (4*0x3b)(r4)
+	mtmsr	r10
+	sync; isync;
+
+	// setup DEC somewhere in the 1/HZ range
+	// if you don't do this, timer interrupt will trigger a few
+	//	seconds later, and that is not good.
+	lis	r3, 0x10
+	mtdec	r3
+
+	// invalidate caches
+	mfspr	r10, SPRN_HID0
+	ori	r5, r10, HID0_ICFI | HID0_DCI
+	mtspr	SPRN_HID0, r5	// invalidate caches
+	sync; isync;
+	mtspr	SPRN_HID0, r10
+	sync; isync;
+
+	// enable caches
+	lwz	r10, (4*0x38)(r4)
+	mtspr	SPRN_HID0, r10	// restore (enable caches, DPM)
+	// ^ this has to be after address translation set in MSR
+	sync
+	isync
+
+
+	// restore 0xf0 (BDI2000)
+	lis	r3, 0xc000
+	lwz	r10, (0x59*4)(r4)
+	stw	r10, 0xf0(r3)
+
+	LOAD_SPRN(LR, 0x3c)
+
+
+	blr
+
+
+// ----------------------------------------------------------------------
+// boring code: helpers
+
+save_regs:
+	// save registers
+	stw	r0, 0(r4)
+	stw	r1, 0x4(r4)
+	stw	r2, 0x8(r4)
+	stw	r11, 0xc(r4)
+	stw	r12, 0x10(r4)
+	stw	r13, 0x14(r4)
+	stw	r14, 0x18(r4)
+	stw	r15, 0x1c(r4)
+	stw	r16, 0x20(r4)
+	stw	r17, 0x24(r4)
+	stw	r18, 0x28(r4)
+	stw	r19, 0x2c(r4)
+	stw	r20, 0x30(r4)
+	stw	r21, 0x34(r4)
+	stw	r22, 0x38(r4)
+	stw	r23, 0x3c(r4)
+	stw	r24, 0x40(r4)
+	stw	r25, 0x44(r4)
+	stw	r26, 0x48(r4)
+	stw	r27, 0x4c(r4)
+	stw	r28, 0x50(r4)
+	stw	r29, 0x54(r4)
+	stw	r30, 0x58(r4)
+	stw	r31, 0x5c(r4)
+
+	// save MMU regs
+	SAVE_SPRN(DBAT0L, 0x18)
+	SAVE_SPRN(DBAT0U, 0x19)
+	SAVE_SPRN(IBAT0L, 0x1a)
+	SAVE_SPRN(IBAT0U, 0x1b)
+	SAVE_SPRN(DBAT1L, 0x1c)
+	SAVE_SPRN(DBAT1U, 0x1d)
+	SAVE_SPRN(IBAT1L, 0x1e)
+	SAVE_SPRN(IBAT1U, 0x1f)
+	SAVE_SPRN(DBAT2L, 0x20)
+	SAVE_SPRN(DBAT2U, 0x21)
+	SAVE_SPRN(IBAT2L, 0x22)
+	SAVE_SPRN(IBAT2U, 0x23)
+	SAVE_SPRN(DBAT3L, 0x24)
+	SAVE_SPRN(DBAT3U, 0x25)
+	SAVE_SPRN(IBAT3L, 0x26)
+	SAVE_SPRN(IBAT3U, 0x27)
+	SAVE_SPRN(DBAT4L, 0x28)
+	SAVE_SPRN(DBAT4U, 0x29)
+	SAVE_SPRN(IBAT4L, 0x2a)
+	SAVE_SPRN(IBAT4U, 0x2b)
+	SAVE_SPRN(DBAT5L, 0x2c)
+	SAVE_SPRN(DBAT5U, 0x2d)
+	SAVE_SPRN(IBAT5L, 0x2e)
+	SAVE_SPRN(IBAT5U, 0x2f)
+	SAVE_SPRN(DBAT6L, 0x30)
+	SAVE_SPRN(DBAT6U, 0x31)
+	SAVE_SPRN(IBAT6L, 0x32)
+	SAVE_SPRN(IBAT6U, 0x33)
+	SAVE_SPRN(DBAT7L, 0x34)
+	SAVE_SPRN(DBAT7U, 0x35)
+	SAVE_SPRN(IBAT7L, 0x36)
+	SAVE_SPRN(IBAT7U, 0x37)
+
+	SAVE_SPRN(HID0, 0x38)
+	SAVE_SPRN(HID1, 0x39)
+	SAVE_SPRN(HID2, 0x3a)
+
+	mfmsr	r10
+	stw	r10, (4*0x3b)(r4)
+
+	//SAVE_SPRN(LR, 0x3c) have to save it before the call
+
+	SAVE_SPRN(SPRG0, 0x40)
+	SAVE_SPRN(SPRG1, 0x41)
+	SAVE_SPRN(SPRG2, 0x42)
+	SAVE_SPRN(SPRG3, 0x43)
+	SAVE_SPRN(SPRG4, 0x44)
+	SAVE_SPRN(SPRG5, 0x45)
+	SAVE_SPRN(SPRG6, 0x46)
+	SAVE_SPRN(SPRG7, 0x47)
+	SAVE_SPRN(IABR,  0x48)
+	SAVE_SPRN(IABR2, 0x49)
+	SAVE_SPRN(DABR,  0x4a)
+	SAVE_SPRN(DABR2, 0x4b)
+	SAVE_SPRN(DMISS, 0x4c)
+	SAVE_SPRN(DCMP,  0x4d)
+	SAVE_SPRN(IMISS, 0x4e)
+	SAVE_SPRN(ICMP,  0x4f)
+	SAVE_SPRN(HASH1, 0x50)
+	SAVE_SPRN(HASH2, 0x51)
+	SAVE_SPRN(MBAR,  0x52)
+
+	SAVE_SPRN(CTR,   0x54)
+	SAVE_SPRN(DSISR, 0x55)
+	SAVE_SPRN(DAR,   0x56)
+	SAVE_SPRN(RPA,   0x57)
+	SAVE_SPRN(SDR1,  0x58)
+
+	SAVE_SPRN(IBCR,  0x5a)
+	SAVE_SPRN(DBCR,  0x5b)
+	SAVE_SPRN(TBRL,  0x5c)
+	SAVE_SPRN(TBRU,  0x5d)
+	// 0x59 reserved by 0xf0
+
+	blr
+
+// restore registers
+restore_regs:
+	lis	r4, registers@h
+	ori	r4, r4, registers@l
+	xoris	r4, r4, 0xc000	// & ~0xc000 0000
+
+	lwz	r0, 0(r4)
+	lwz	r1, 0x4(r4)
+	lwz	r2, 0x8(r4)
+	lwz	r11, 0xc(r4)
+	lwz	r12, 0x10(r4)
+	lwz	r13, 0x14(r4)
+	lwz	r14, 0x18(r4)
+	lwz	r15, 0x1c(r4)
+	lwz	r16, 0x20(r4)
+	lwz	r17, 0x24(r4)
+	lwz	r18, 0x28(r4)
+	lwz	r19, 0x2c(r4)
+	lwz	r20, 0x30(r4)
+	lwz	r21, 0x34(r4)
+	lwz	r22, 0x38(r4)
+	lwz	r23, 0x3c(r4)
+	lwz	r24, 0x40(r4)
+	lwz	r25, 0x44(r4)
+	lwz	r26, 0x48(r4)
+	lwz	r27, 0x4c(r4)
+	lwz	r28, 0x50(r4)
+	lwz	r29, 0x54(r4)
+	lwz	r30, 0x58(r4)
+	lwz	r31, 0x5c(r4)
+
+	// restore MMU regs
+	LOAD_SPRN(DBAT0L, 0x18)
+	LOAD_SPRN(DBAT0U, 0x19)
+	LOAD_SPRN(IBAT0L, 0x1a)
+	LOAD_SPRN(IBAT0U, 0x1b)
+	LOAD_SPRN(DBAT1L, 0x1c)
+	LOAD_SPRN(DBAT1U, 0x1d)
+	LOAD_SPRN(IBAT1L, 0x1e)
+	LOAD_SPRN(IBAT1U, 0x1f)
+	LOAD_SPRN(DBAT2L, 0x20)
+	LOAD_SPRN(DBAT2U, 0x21)
+	LOAD_SPRN(IBAT2L, 0x22)
+	LOAD_SPRN(IBAT2U, 0x23)
+	LOAD_SPRN(DBAT3L, 0x24)
+	LOAD_SPRN(DBAT3U, 0x25)
+	LOAD_SPRN(IBAT3L, 0x26)
+	LOAD_SPRN(IBAT3U, 0x27)
+	LOAD_SPRN(DBAT4L, 0x28)
+	LOAD_SPRN(DBAT4U, 0x29)
+	LOAD_SPRN(IBAT4L, 0x2a)
+	LOAD_SPRN(IBAT4U, 0x2b)
+	LOAD_SPRN(DBAT5L, 0x2c)
+	LOAD_SPRN(DBAT5U, 0x2d)
+	LOAD_SPRN(IBAT5L, 0x2e)
+	LOAD_SPRN(IBAT5U, 0x2f)
+	LOAD_SPRN(DBAT6L, 0x30)
+	LOAD_SPRN(DBAT6U, 0x31)
+	LOAD_SPRN(IBAT6L, 0x32)
+	LOAD_SPRN(IBAT6U, 0x33)
+	LOAD_SPRN(DBAT7L, 0x34)
+	LOAD_SPRN(DBAT7U, 0x35)
+	LOAD_SPRN(IBAT7L, 0x36)
+	LOAD_SPRN(IBAT7U, 0x37)
+
+	/* these are a bit tricky */
+	/*
+	0x38 - HID0
+	0x39 - HID1
+	0x3a - HID2
+	0x3b - MSR
+	0x3c - LR
+	*/
+
+	// rest of regs
+	LOAD_SPRN(SPRG0, 0x40);
+	LOAD_SPRN(SPRG1, 0x41);
+	LOAD_SPRN(SPRG2, 0x42);
+	LOAD_SPRN(SPRG3, 0x43);
+	LOAD_SPRN(SPRG4, 0x44);
+	LOAD_SPRN(SPRG5, 0x45);
+	LOAD_SPRN(SPRG6, 0x46);
+	LOAD_SPRN(SPRG7, 0x47);
+	LOAD_SPRN(IABR,  0x48);
+	LOAD_SPRN(IABR2, 0x49);
+	LOAD_SPRN(DABR,  0x4a);
+	LOAD_SPRN(DABR2, 0x4b);
+	LOAD_SPRN(DMISS, 0x4c);
+	LOAD_SPRN(DCMP,  0x4d);
+	LOAD_SPRN(IMISS, 0x4e);
+	LOAD_SPRN(ICMP,  0x4f);
+	LOAD_SPRN(HASH1, 0x50);
+	LOAD_SPRN(HASH2, 0x51);
+	LOAD_SPRN(MBAR,  0x52);
+
+	LOAD_SPRN(CTR,   0x54);
+	LOAD_SPRN(DSISR, 0x55);
+	LOAD_SPRN(DAR,   0x56);
+	LOAD_SPRN(RPA,   0x57);
+	LOAD_SPRN(SDR1,  0x58);
+
+	// 0x59 reserved by 0xf0
+	LOAD_SPRN(IBCR,  0x5a);
+	LOAD_SPRN(DBCR,  0x5b);
+	LOAD_SPRN(TBWL,  0x5c);	// these two have separate R/W regs
+	LOAD_SPRN(TBWU,  0x5d);
+
+	blr
+
+
+
+#if 1
+/* cache flushing code. copied from boot/util.S. how do you link with that?! */
+#define NUM_CACHE_LINES 128*8
+#define cache_flush_buffer 0xc0000000
+
+/*
+ * Flush data cache
+ * Do this by just reading lots of stuff into the cache.
+ */
+        .globl flush_data_cache
+flush_data_cache:
+	lis	r3,cache_flush_buffer@h
+	ori	r3,r3,cache_flush_buffer@l
+	li	r4,NUM_CACHE_LINES
+	mtctr	r4
+1:
+	lwz	r4,0(r3)
+	addi	r3,r3,L1_CACHE_BYTES	/* Next line, please */
+	bdnz	1b
+	blr
+#endif

[-- Attachment #8: suspend_arch_code_non-init --]
[-- Type: text/plain, Size: 1568 bytes --]

Index: work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/mpc52xx_setup.c
+++ work-powerpc.git/arch/ppc/syslib/mpc52xx_setup.c
@@ -220,8 +220,7 @@ mpc52xx_calibrate_decr(void)
 	tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
 }
 
-void __init
-mpc52xx_setup_cpu(void)
+void mpc52xx_setup_cpu(void)
 {
 	struct mpc52xx_cdm  __iomem *cdm;
 	struct mpc52xx_xlb  __iomem *xlb;
Index: work-powerpc.git/arch/ppc/platforms/lite5200.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/lite5200.c
+++ work-powerpc.git/arch/ppc/platforms/lite5200.c
@@ -93,8 +93,7 @@ lite5200_map_irq(struct pci_dev *dev, un
 #endif
 #endif
 
-static void __init
-lite5200_setup_cpu(void)
+void lite5200_setup_cpu(void)
 {
 	struct mpc52xx_gpio __iomem *gpio;
 	struct mpc52xx_intr __iomem *intr;
@@ -149,8 +148,7 @@ unmap_regs:
 	if (intr) iounmap(intr);
 }
 
-static void __init
-lite5200_setup_arch(void)
+static void lite5200_setup_arch(void)
 {
 	/* CPU & Port mux setup */
 	mpc52xx_setup_cpu();	/* Generic */
Index: work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/lite5200_pm.c
+++ work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
@@ -48,6 +48,8 @@ static int l5200_pm_enter(suspend_state_
 	gpio->port_config = gpio_saved_pc;
 	iounmap(gpio);
 
+	ppc_md.setup_arch();
+
 	return 0;
 }
 

[-- Attachment #9: suspend_pic --]
[-- Type: text/plain, Size: 2599 bytes --]

Index: work-powerpc.git/arch/ppc/syslib/mpc52xx_pic.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/mpc52xx_pic.c
+++ work-powerpc.git/arch/ppc/syslib/mpc52xx_pic.c
@@ -317,3 +317,47 @@ peripheral:
 	return irq;
 }
 
+/* save and restore registers for suspend to ram */
+u32 pic_regs[0x10];
+void mpc52xx_pic_suspend(void)
+{
+	int i = 0;
+	// sdma into sdma suspend/resume
+	pic_regs[i++] = in_be32(&sdma->IntPend);
+	pic_regs[i++] = in_be32(&sdma->IntMask);
+	pic_regs[i++] = in_be32(&intr->per_mask);
+	pic_regs[i++] = in_be32(&intr->main_mask);
+	pic_regs[i++] = in_be32(&intr->ctrl);
+	pic_regs[i++] = in_be32(&intr->per_pri1);
+	pic_regs[i++] = in_be32(&intr->per_pri2);
+	pic_regs[i++] = in_be32(&intr->per_pri3);
+	pic_regs[i++] = in_be32(&intr->main_pri1);
+	pic_regs[i++] = in_be32(&intr->main_pri2);
+
+	iounmap(intr);
+	iounmap(sdma);
+	iounmap(slt0);
+}
+
+void mpc52xx_pic_resume(void)
+{
+	int i = 0;
+
+	intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
+	sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
+	slt0 = ioremap(MPC52xx_PA(MPC52xx_SLTx_OFFSET(0)), MPC52xx_SLT_SIZE);
+	if ((intr==NULL) || (sdma==NULL) || (slt0==NULL))
+		panic("Can't ioremap PIC/SDMA/SLT0 registers for init_irq !");
+
+	// sdma into sdma suspend/resume
+	out_be32(&sdma->IntPend, pic_regs[i++]);
+	out_be32(&sdma->IntMask, pic_regs[i++]);
+	out_be32(&intr->per_mask, pic_regs[i++]);
+	out_be32(&intr->main_mask, pic_regs[i++]);
+	out_be32(&intr->ctrl, pic_regs[i++]);
+	out_be32(&intr->per_pri1, pic_regs[i++]);
+	out_be32(&intr->per_pri2, pic_regs[i++]);
+	out_be32(&intr->per_pri3, pic_regs[i++]);
+	out_be32(&intr->main_pri1, pic_regs[i++]);
+	out_be32(&intr->main_pri2, pic_regs[i++]);
+}
Index: work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/platforms/lite5200_pm.c
+++ work-powerpc.git/arch/ppc/platforms/lite5200_pm.c
@@ -23,6 +23,9 @@ static int l5200_pm_prepare(suspend_stat
 	return 0;
 }
 
+extern void mpc52xx_pic_suspend(void);
+extern void mpc52xx_pic_resume(void);
+
 static int l5200_pm_enter(suspend_state_t state)
 {
 	struct mpc52xx_gpio __iomem *gpio;
@@ -40,6 +43,8 @@ static int l5200_pm_enter(suspend_state_
 	gpio_saved_pc = gpio->port_config;
 	iounmap(gpio);
 
+	mpc52xx_pic_suspend();
+
 	if (state == PM_SUSPEND_MEM)
 		l5200_qt_sleep();
 
@@ -50,6 +55,8 @@ static int l5200_pm_enter(suspend_state_
 
 	ppc_md.setup_arch();
 
+	mpc52xx_pic_resume();
+
 	return 0;
 }
 

[-- Attachment #10: uart_smaller_delay --]
[-- Type: text/plain, Size: 660 bytes --]

Index: work-powerpc.git/drivers/serial/mpc52xx_uart.c
===================================================================
--- work-powerpc.git.orig/drivers/serial/mpc52xx_uart.c
+++ work-powerpc.git/drivers/serial/mpc52xx_uart.c
@@ -285,7 +285,7 @@ mpc52xx_uart_set_termios(struct uart_por
 
 	/* Do our best to flush TX & RX, so we don't loose anything */
 	/* But we don't wait indefinitly ! */
-	j = 5000000;	/* Maximum wait */
+	j = 500000;	/* Maximum wait */
 	/* FIXME Can't receive chars since set_termios might be called at early
 	 * boot for the console, all stuff is not yet ready to receive at that
 	 * time and that just makes the kernel oops */

[-- Attachment #11: suspend_bestcomm --]
[-- Type: text/plain, Size: 3387 bytes --]

Index: work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
===================================================================
--- work-powerpc.git.orig/arch/ppc/syslib/bestcomm/bestcomm.c
+++ work-powerpc.git/arch/ppc/syslib/bestcomm/bestcomm.c
@@ -360,16 +360,85 @@ out:
 }
 
 
+/* saved sdma regs */
+struct mpc52xx_sdma sdma_regs;
+char saved_sram[0x4000]; // 16 kB
+static int mpc52xx_sdma_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int i;
+
+	/* don't actually need the be variants */
+	sdma_regs.taskBar =	in_be32(&sdma.io->taskBar);
+	sdma_regs.currentPointer = in_be32(&sdma.io->currentPointer);
+	sdma_regs.endPointer =	in_be32(&sdma.io->endPointer);
+	sdma_regs.variablePointer = in_be32(&sdma.io->variablePointer);
+	sdma_regs.IntVect1 =	in_8(&sdma.io->IntVect1);
+	sdma_regs.IntVect2 =	in_8(&sdma.io->IntVect2);
+	sdma_regs.PtdCntrl =	in_be16(&sdma.io->PtdCntrl);
+	sdma_regs.IntPend =	in_be32(&sdma.io->IntPend);
+	sdma_regs.IntMask =	in_be32(&sdma.io->IntMask);
+
+	for (i=0; i<16; i++)
+		sdma_regs.tcr[i] = in_be16(&sdma.io->tcr[i]);
+	for (i=0; i<32; i++)
+		sdma_regs.ipr[i] = in_8(&sdma.io->ipr[i]);
+
+	sdma_regs.cReqSelect =	in_be32(&sdma.io->cReqSelect);
+	sdma_regs.task_size[0] = in_be32(&sdma.io->task_size[0]);
+	sdma_regs.task_size[1] = in_be32(&sdma.io->task_size[1]);
+	sdma_regs.MDEDebug =	in_be32(&sdma.io->MDEDebug);
+	sdma_regs.Value1 =	in_be32(&sdma.io->Value1);
+	sdma_regs.Value2 =	in_be32(&sdma.io->Value2);
+	sdma_regs.Control =	in_be32(&sdma.io->Control);
+	sdma_regs.Status =	in_be32(&sdma.io->Status);
+	sdma_regs.PTDDebug =	in_be32(&sdma.io->PTDDebug);
+
+	memcpy(saved_sram, sdma.sram, sdma.sram_size);
+	return 0;
+}
+
+static int mpc52xx_sdma_resume(struct platform_device *pdev)
+{
+	int i;
+
+	memcpy(sdma.sram, saved_sram, sdma.sram_size);
+
+	// XXX order?
+	out_be32(&sdma.io->taskBar, sdma_regs.taskBar);
+	out_be32(&sdma.io->currentPointer, sdma_regs.currentPointer);
+	out_be32(&sdma.io->endPointer, sdma_regs.endPointer);
+	out_be32(&sdma.io->variablePointer, sdma_regs.variablePointer);
+	out_8(&sdma.io->IntVect1, sdma_regs.IntVect1);
+	out_8(&sdma.io->IntVect2, sdma_regs.IntVect2);
+	out_be16(&sdma.io->PtdCntrl, sdma_regs.PtdCntrl);
+	out_be32(&sdma.io->IntPend, sdma_regs.IntPend);
+	out_be32(&sdma.io->IntMask, sdma_regs.IntMask);
+
+	for (i=0; i<16; i++)
+		out_be16(&sdma.io->tcr[i], sdma_regs.tcr[i]);
+	for (i=0; i<32; i++)
+		out_8(&sdma.io->ipr[i], sdma_regs.ipr[i]);
+
+	out_be32(&sdma.io->cReqSelect, sdma_regs.cReqSelect);
+	out_be32(&sdma.io->task_size[0], sdma_regs.task_size[0]);
+	out_be32(&sdma.io->task_size[1], sdma_regs.task_size[1]);
+	out_be32(&sdma.io->MDEDebug, sdma_regs.MDEDebug);
+	out_be32(&sdma.io->Value1, sdma_regs.Value1);
+	out_be32(&sdma.io->Value2, sdma_regs.Value2);
+	out_be32(&sdma.io->Control, sdma_regs.Control);
+	out_be32(&sdma.io->Status, sdma_regs.Status);
+	out_be32(&sdma.io->PTDDebug, sdma_regs.PTDDebug);
+	return 0;
+}
+
 static struct platform_driver mpc52xx_sdma_driver = {
 	.probe 	  = mpc52xx_sdma_probe,
 /*	.remove	  = mpc52xx_sdma_remove,	TODO */
-#ifdef CONFIG_PM
-/*	.suspend	= mpc52xx_sdma_suspend,	TODO */
-/*	.resume		= mpc52xx_sdma_resume,	TODO */
-#endif
-	.driver	= {
-		.owner	  = THIS_MODULE,
+	.suspend	= mpc52xx_sdma_suspend,
+	.resume		= mpc52xx_sdma_resume,
+	.driver = {
 		.name	  = DRIVER_NAME,
+		.owner	  = THIS_MODULE,
 	},
 };
 

[-- Attachment #12: suspend_fec --]
[-- Type: text/plain, Size: 2415 bytes --]

Index: work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
===================================================================
--- work-powerpc.git.orig/drivers/net/fec_mpc52xx/fec.c
+++ work-powerpc.git/drivers/net/fec_mpc52xx/fec.c
@@ -731,14 +731,55 @@ mpc52xx_fec_remove(struct platform_devic
 	return 0;
 }
 
+
+extern int mpc52xx_fec_mii_suspend(struct net_device *dev);
+int mpc52xx_fec_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct fec_priv *priv = ndev->priv;
+	struct mpc52xx_fec *fec = priv->fec;
+
+	netif_stop_queue(ndev);
+	out_be32(&fec->imask, 0x0);
+
+	/* Disable the rx and tx tasks. */
+	sdma_disable(priv->rx_sdma);
+	sdma_disable(priv->tx_sdma);
+
+	/* Stop FEC */
+	out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~0x2);
+
+	mpc52xx_fec_mii_suspend(ndev);
+
+	return 0;
+}
+
+int mpc52xx_fec_resume(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct fec_priv *priv = ndev->priv;
+	//struct mpc52xx_fec *fec = priv->fec;
+
+	/* Restart the DMA tasks */
+	sdma_fec_rx_init(priv->rx_sdma, priv->rx_fifo, FEC_RX_BUFFER_SIZE);
+	sdma_fec_tx_init(priv->tx_sdma, priv->tx_fifo);
+	fec_hw_init(ndev);
+	fec_mii_init(ndev);
+
+	if (priv->sequence_done) {		 /* redo the fec_open() */
+		fec_free_rx_buffers(priv->rx_sdma);
+		fec_open(ndev);
+	}
+
+	return 0;
+}
+
 static struct platform_driver mpc52xx_fec_driver = {
 	.probe		= mpc52xx_fec_probe,
 	.remove		= mpc52xx_fec_remove,
-#ifdef CONFIG_PM
-/*	.suspend	= mpc52xx_fec_suspend,	TODO */
-/*	.resume		= mpc52xx_fec_resume,	TODO */
-#endif
-	.driver	= {
+	.suspend	= mpc52xx_fec_suspend,
+	.resume		= mpc52xx_fec_resume,
+	.driver = {
 		.name	  = DRIVER_NAME,
 	},
 };
Index: work-powerpc.git/drivers/net/fec_mpc52xx/fec_phy.c
===================================================================
--- work-powerpc.git.orig/drivers/net/fec_mpc52xx/fec_phy.c
+++ work-powerpc.git/drivers/net/fec_mpc52xx/fec_phy.c
@@ -521,6 +521,15 @@ int fec_mii_wait(struct net_device *dev)
 	return 0;
 }
 
+
+phy_cmd_t phy_cmd_powerdown[] =  { { mk_mii_write(0x00, 0x0800), NULL }, // power down
+				{ mk_mii_end, } };
+
+void mpc52xx_fec_mii_suspend(struct net_device *dev)
+{
+	mii_do_cmd(dev, phy_cmd_powerdown);
+}
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Dale Farnsworth");
 MODULE_DESCRIPTION("PHY driver for Motorola MPC52xx FEC");


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

* Re: [RFC PATCHES] lite5200b low power mode works
  2007-01-23  9:33 ` [RFC PATCHES] lite5200b low power mode works Domen Puncer
@ 2007-02-19  7:21   ` Domen Puncer
  2007-02-19 10:15     ` Sylvain Munaut
  0 siblings, 1 reply; 7+ messages in thread
From: Domen Puncer @ 2007-02-19  7:21 UTC (permalink / raw)
  To: linuxppc-embedded; +Cc: Sylvain Munaut

On 23/01/07 10:33 +0100, Domen Puncer wrote:
> 
> I'm planning on porting this to Grant/Sylvain's arch/powerpc mpc52xx
> tree soon.

OK... and I've done that (mainline kernel + FEC/bestcomm patches)
+ some cleanups, fixes.

Any of you guys interested in having this in your tree?


	Domen

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

* Re: [RFC PATCHES] lite5200b low power mode works
  2007-02-19  7:21   ` Domen Puncer
@ 2007-02-19 10:15     ` Sylvain Munaut
  2007-02-19 11:00       ` Domen Puncer
  0 siblings, 1 reply; 7+ messages in thread
From: Sylvain Munaut @ 2007-02-19 10:15 UTC (permalink / raw)
  To: Domen Puncer; +Cc: linuxppc-embedded

Domen Puncer wrote:
> On 23/01/07 10:33 +0100, Domen Puncer wrote:
>   
>> I'm planning on porting this to Grant/Sylvain's arch/powerpc mpc52xx
>> tree soon.
>>     
>
> OK... and I've done that (mainline kernel + FEC/bestcomm patches)
> + some cleanups, fixes.
>
> Any of you guys interested in having this in your tree?
>
>   
Sure, feel free to post them.

    Sylvain

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

* Re: [RFC PATCHES] lite5200b low power mode works
  2007-02-19 10:15     ` Sylvain Munaut
@ 2007-02-19 11:00       ` Domen Puncer
  2007-02-19 11:01         ` Sylvain Munaut
  0 siblings, 1 reply; 7+ messages in thread
From: Domen Puncer @ 2007-02-19 11:00 UTC (permalink / raw)
  To: Sylvain Munaut; +Cc: linuxppc-embedded

On 19/02/07 11:15 +0100, Sylvain Munaut wrote:
> Domen Puncer wrote:
> > On 23/01/07 10:33 +0100, Domen Puncer wrote:
> >   
> >> I'm planning on porting this to Grant/Sylvain's arch/powerpc mpc52xx
> >> tree soon.
> >>     
> >
> > OK... and I've done that (mainline kernel + FEC/bestcomm patches)
> > + some cleanups, fixes.
> >
> > Any of you guys interested in having this in your tree?
> >
> >   
> Sure, feel free to post them.

It seems to be my lucky day. I just figured out what I was doing
wrong with deep-sleep (aplicable to all mpc52xx), so I'll delay this
a few days, and send patches implementing both mpc5200b and lite5200b
"suspend to ram" modes.


	Domen

> 
>     Sylvain

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

* Re: [RFC PATCHES] lite5200b low power mode works
  2007-02-19 11:00       ` Domen Puncer
@ 2007-02-19 11:01         ` Sylvain Munaut
  0 siblings, 0 replies; 7+ messages in thread
From: Sylvain Munaut @ 2007-02-19 11:01 UTC (permalink / raw)
  To: Domen Puncer; +Cc: linuxppc-embedded

Domen Puncer wrote:
> On 19/02/07 11:15 +0100, Sylvain Munaut wrote:
>   
>> Domen Puncer wrote:
>>     
>>> On 23/01/07 10:33 +0100, Domen Puncer wrote:
>>>   
>>>       
>>>> I'm planning on porting this to Grant/Sylvain's arch/powerpc mpc52xx
>>>> tree soon.
>>>>     
>>>>         
>>> OK... and I've done that (mainline kernel + FEC/bestcomm patches)
>>> + some cleanups, fixes.
>>>
>>> Any of you guys interested in having this in your tree?
>>>
>>>   
>>>       
>> Sure, feel free to post them.
>>     
>
> It seems to be my lucky day. I just figured out what I was doing
> wrong with deep-sleep (aplicable to all mpc52xx), so I'll delay this
> a few days, and send patches implementing both mpc5200b and lite5200b
> "suspend to ram" modes.
>   
Even better !

    Sylvain

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

end of thread, other threads:[~2007-02-19 11:02 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-12-22 10:54 [RFC] lite5200b low power mode Domen Puncer
2006-12-22 11:10 ` Sylvain Munaut
2007-01-23  9:33 ` [RFC PATCHES] lite5200b low power mode works Domen Puncer
2007-02-19  7:21   ` Domen Puncer
2007-02-19 10:15     ` Sylvain Munaut
2007-02-19 11:00       ` Domen Puncer
2007-02-19 11:01         ` Sylvain Munaut

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.