Subject: [PATCH] [1/2] PM support for Ebony

From: Geoff Levand <[email-masked]>
Date: Fri, 03 Jun 2005 16:22:40 -0700

I rebased this to apply to Benjamin's ppc32-rework-pm.diff, but 
didn't recode it to take advantage of the extra hooks.  More work 
is certainly needed for wake-on-lan.  Any comments on improvement 
would be most welcome.

I could also make one available against a 2.6.12-rc if requested.

-Geoff

* pm-on-ebony.patch 

This patch provides power management support for the IBM PPC440GP Ebony 
Reference Platform.  The main portion of the patch implements the platform 
specific pm_ops structure required by the kernel power management sub-system.  
The current implementation only supports suspend-to-memory (PM_SUSPEND_MEM), 
though unpublished suspend-to-disk work has been started.

This implementation arranges for the U44 switch on the Ebony platform, 
connected to the SMI interrupt handler, to be used as a system resume trigger.

Signed-off-by: Geoff Levand <[email-masked]> for CELF

--
Index: linux-2.6.12-bhpm/arch/ppc/platforms/4xx/Kconfig
===================================================================
--- linux-2.6.12-bhpm.orig/arch/ppc/platforms/4xx/Kconfig	2005-06-03 16:14:44.000000000 -0700
+++ linux-2.6.12-bhpm/arch/ppc/platforms/4xx/Kconfig	2005-06-03 16:15:07.000000000 -0700
@@ -214,10 +214,6 @@
 	depends on 4xx
 	default y
 
-config PM
-	bool "Power Management support (EXPERIMENTAL)"
-	depends on 4xx && EXPERIMENTAL
-
 choice
 	prompt "TTYS0 device and default console"
 	depends on 40x
Index: linux-2.6.12-bhpm/arch/ppc/platforms/4xx/Makefile
===================================================================
--- linux-2.6.12-bhpm.orig/arch/ppc/platforms/4xx/Makefile	2005-06-03 16:14:44.000000000 -0700
+++ linux-2.6.12-bhpm/arch/ppc/platforms/4xx/Makefile	2005-06-03 16:15:07.000000000 -0700
@@ -25,3 +25,6 @@
 obj-$(CONFIG_405EP)		+= ibm405ep.o
 obj-$(CONFIG_405GPR)		+= ibm405gpr.o
 obj-$(CONFIG_VIRTEX_II_PRO)	+= virtex-ii_pro.o
+ifeq ($(CONFIG_PM),y)
+obj-$(CONFIG_EBONY)		+= ebony_pm.o ibm440gp_sleep.o
+endif
Index: linux-2.6.12-bhpm/arch/ppc/platforms/4xx/ebony_pm.c
===================================================================
--- linux-2.6.12-bhpm.orig/arch/ppc/platforms/4xx/ebony_pm.c	2005-06-01 08:52:49.947684744 -0700
+++ linux-2.6.12-bhpm/arch/ppc/platforms/4xx/ebony_pm.c	2005-06-03 16:15:07.000000000 -0700
@@ -0,0 +1,202 @@
+/*
+ * ebony_pm.c - This file contains the PM functions for Ebony.
+ *
+ *  Copyright 2004 Sony Corp.
+ *
+ *  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; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/types.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <asm/ibm44x.h>
+
+#define IBM_CPM_ALL        (IBM_CPM_IIC0 | IBM_CPM_IIC1 | IBM_CPM_PCI | \
+                       IBM_CPM_CPU | IBM_CPM_DMA | IBM_CPM_BGO | IBM_CPM_BGI |  \
+                       IBM_CPM_EBC | IBM_CPM_EBM | IBM_CPM_DMC | IBM_CPM_PLB | IBM_CPM_SRAM  | \
+                       IBM_CPM_PPM | IBM_CPM_UIC1 | IBM_CPM_GPIO0 | IBM_CPM_UART0 | IBM_CPM_UART1 | \
+                       IBM_CPM_UIC0 | IBM_CPM_TMRCLK )
+
+#define UIC0_EIR5_BIT (1<<(31-28))	/* External Intr 5  == SMI */
+#define UIC0_UIC1NC_BIT (1<<(31-30))	
+
+extern void serial8250_suspend_port_busy(int line);
+extern void serial8250_resume_port_busy(int line);
+
+void ibm440gp_sleep(__u32 CPM, __u32 MSR_OR);
+
+#define DEBUG
+
+#ifdef	DEBUG
+
+/* #define  USE_ETHER_TO_RESUME */
+
+static int __tm_printk(const char *fmt, ...)
+{
+        char buf[512];
+        va_list args;
+        int i;
+        unsigned long long tt;
+        unsigned long us, ms;
+
+        tt = sched_clock();
+        ms = tt >> 10;  /* convert to usec */
+        us = ms % 1000;
+        ms = ms / 1000;
+
+        i = sprintf(buf, "%6.6lu.%3.3lums:", ms,us);
+        va_start(args, fmt);
+        i = vsnprintf(buf+i, sizeof(buf)-i, fmt, args);
+        va_end(args);
+        printk(buf);
+
+        return i;
+}
+
+#endif /* DEBUG */
+
+
+/*
+ * PM ops for EBONY board.
+ */
+
+static int ebony_pm_enter(suspend_state_t state)
+{
+	__u32 uic_save_er;
+	__u32 save_msr;
+	__u32 cpm_save_er;
+	__u32 cpm_er;
+
+	if (state != PM_SUSPEND_MEM)
+		return -EINVAL;
+
+	/*  Save MSR and Stop all interrupts */
+	save_msr = mfmsr();
+	_nmask_and_or_msr((MSR_CE|MSR_EE), 0);
+
+	/* save current CPM */
+	cpm_save_er = mfdcr(DCRN_CPC0_ER);
+
+	/* save UIC0 enable registers */
+	uic_save_er = mfdcr(DCRN_UIC_ER(UIC0));
+
+#ifdef USE_ETHER_TO_RESUME
+	mtdcr(DCRN_UIC_ER(UIC0), UIC0_EIR5_BIT|UIC0_UIC1NC_BIT);
+#else
+	/* mask UIC0 interrupts, except External Intr #5 */
+	mtdcr(DCRN_UIC_ER(UIC0), UIC0_EIR5_BIT);
+#endif
+
+#ifdef	DEBUG
+	__tm_printk("UIC0_ER:0x%8.8x -> 0x%8.8x\n",uic_save_er, mfdcr(DCRN_UIC_ER(UIC0)));
+#endif
+
+	/* set up CPM */
+	cpm_er = IBM_CPM_ALL & ~IBM_CPM_UIC0;
+#ifdef USE_ETHER_TO_RESUME
+	cpm_er &= ~CPM_UIC1;
+#endif
+
+#ifdef	DEBUG
+	__tm_printk("UIC0_SR:0x%8.8x\n", mfdcr(DCRN_UIC_SR(UIC0)));
+	__tm_printk("CPM_ER:0x%8.8x\n", cpm_er);
+	__tm_printk("SLEEP\n");
+#endif
+
+	/* we need this to work with printk on serial console */
+	serial8250_suspend_port_busy(0);
+
+	/* Enable interrupts and Enter SLEEP mode */
+	ibm440gp_sleep(cpm_er, (MSR_EE|MSR_WE));
+
+	/*  Stop all interrupts, again */
+	_nmask_and_or_msr((MSR_CE|MSR_EE), 0);
+
+	/* Restore CPM, before resume serials for printk() */
+	mtdcr(DCRN_CPC0_ER, cpm_save_er);
+
+	/* we need this to work with printk on serial console */
+	serial8250_resume_port_busy(0);
+
+	__tm_printk("WAKEUP\n");
+
+	/* Restore UIC0 enable registers */
+	mtdcr(DCRN_UIC_ER(UIC0), uic_save_er);
+
+	/* Restore MSR */
+	mtmsr(save_msr);
+
+	return 0;
+}
+
+static int ebony_pm_prepare(suspend_state_t state)
+{
+
+	if (state != PM_SUSPEND_MEM)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ebony_pm_finish(suspend_state_t state)
+{
+	return 0;
+}
+
+static struct pm_ops ebony_pm_ops = {
+	.pm_disk_mode	= PM_DISK_FIRMWARE,
+	.prepare	= ebony_pm_prepare,
+	.enter		= ebony_pm_enter,
+	.finish		= ebony_pm_finish,
+};
+
+static int __init ebony_pm_init(void)
+{
+	pm_set_ops(&ebony_pm_ops);
+	return 0;
+}
+
+late_initcall(ebony_pm_init);
+
+/*
+ * SMI handler for resume
+ */
+
+#define PPC440GP_EXT_INT5_INTR	28	/* SMI: See ebony.c */
+
+static irqreturn_t 
+smi_handler(int cpl, void *dev_id, struct pt_regs *regs)
+{
+#ifdef	DEBUG
+	printk("SMI INTR\n");
+#endif
+	return IRQ_HANDLED;
+}
+
+static int __init  init_smi(void)
+{
+	/* Enable SMI interrupt */
+	if (request_irq(PPC440GP_EXT_INT5_INTR, smi_handler, 
+				SA_INTERRUPT, "SMI", NULL)) {
+		printk(KERN_ERR "SMI: cannot register IRQ:%d\n", PPC440GP_EXT_INT5_INTR);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+late_initcall(init_smi);
+
+
Index: linux-2.6.12-bhpm/arch/ppc/platforms/4xx/ibm440gp_sleep.S
===================================================================
--- linux-2.6.12-bhpm.orig/arch/ppc/platforms/4xx/ibm440gp_sleep.S	2005-06-01 08:52:49.947684744 -0700
+++ linux-2.6.12-bhpm/arch/ppc/platforms/4xx/ibm440gp_sleep.S	2005-06-03 16:15:07.000000000 -0700
@@ -0,0 +1,44 @@
+/*
+ * ibm440gp_sleep.S - This file contains the sleep function for 440GP CPU.
+ *
+ *  Copyright 2004 Sony Corp.
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+
+
+	.text
+
+/*
+ * ibm440gp_sleep (CPC0_ER_VAL, MSR_OR_VAL)
+ * 	r3: new CPC0_ER value
+ * 	r4: MSR value to be OR
+ */
+_GLOBAL(ibm440gp_sleep)
+	mfmsr	r0
+	or	r0,r0,r4
+	sync
+	mtdcr	177,r3	// 177: DCRN_CPC0_ER
+	sync
+	mtmsr	r0
+	sync
+
+	blr
+
+
Index: linux-2.6.12-bhpm/include/asm-ppc/ocp.h
===================================================================
--- linux-2.6.12-bhpm.orig/include/asm-ppc/ocp.h	2005-06-03 16:14:44.000000000 -0700
+++ linux-2.6.12-bhpm/include/asm-ppc/ocp.h	2005-06-03 16:15:07.000000000 -0700
@@ -151,13 +151,21 @@
 static inline void
 ocp_force_power_off(struct ocp_device *odev)
 {
+#ifdef CONFIG_44x
+	mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) | odev->def->pm);
+#else
 	mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | odev->def->pm);
+#endif
 }
 
 static inline void
 ocp_force_power_on(struct ocp_device *odev)
 {
+#ifdef CONFIG_44x
+	mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) & ~odev->def->pm);
+#else
 	mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) & ~odev->def->pm);
+#endif
 }
 #else
 #define ocp_force_power_off(x)	(void)(x)

-EOF