[Blackfin] arch: hook up set_irq_wake in Blackfin's irq code

 - Add support for irq_wake on system and gpio interrupts
 - Remove outdated kernel options
 - Add option to select default PM mode
 - Fix various places where SIC_IWRx was only handled partially

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>

diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index ba21e33..368bc7f 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -544,7 +544,7 @@
 	default y
 	help
 	  If enabled, the entire ASM lowlevel exception and interrupt entry code
-	  (STORE/RESTORE CONTEXT) is linked into L1 instruction memory. 
+	  (STORE/RESTORE CONTEXT) is linked into L1 instruction memory.
 	  (less latency)
 
 config DO_IRQ_L1
@@ -904,29 +904,38 @@
 	depends on !SMP
 
 choice
-	prompt "Select PM Wakeup Event Source"
-	default PM_WAKEUP_GPIO_BY_SIC_IWR
+	prompt "Default Power Saving Mode"
 	depends on PM
+	default PM_BFIN_SLEEP_DEEPER
+config  PM_BFIN_SLEEP_DEEPER
+	bool "Sleep Deeper"
 	help
-	  If you have a GPIO already configured as input with the corresponding PORTx_MASK
-	  bit set - "Specify Wakeup Event by SIC_IWR value"
+	  Sleep "Deeper" Mode (High Power Savings) - This mode reduces dynamic
+	  power dissipation by disabling the clock to the processor core (CCLK).
+	  Furthermore, Standby sets the internal power supply voltage (VDDINT)
+	  to 0.85 V to provide the greatest power savings, while preserving the
+	  processor state.
+	  The PLL and system clock (SCLK) continue to operate at a very low
+	  frequency of about 3.3 MHz. To preserve data integrity in the SDRAM,
+	  the SDRAM is put into Self Refresh Mode. Typically an external event
+	  such as GPIO interrupt or RTC activity wakes up the processor.
+	  Various Peripherals such as UART, SPORT, PPI may not function as
+	  normal during Sleep Deeper, due to the reduced SCLK frequency.
+	  When in the sleep mode, system DMA access to L1 memory is not supported.
 
-config PM_WAKEUP_GPIO_BY_SIC_IWR
-	bool "Specify Wakeup Event by SIC_IWR value"
-config PM_WAKEUP_BY_GPIO
-	bool "Cause Wakeup Event by GPIO"
-config PM_WAKEUP_GPIO_API
-	bool "Configure Wakeup Event by PM GPIO API"
-
+config  PM_BFIN_SLEEP
+	bool "Sleep"
+	help
+	  Sleep Mode (High Power Savings) - The sleep mode reduces power
+	  dissipation by disabling the clock to the processor core (CCLK).
+	  The PLL and system clock (SCLK), however, continue to operate in
+	  this mode. Typically an external event or RTC activity will wake
+	  up the processor. When in the sleep mode,
+	  system DMA access to L1 memory is not supported.
 endchoice
 
-config PM_WAKEUP_SIC_IWR
-	hex "Wakeup Events (SIC_IWR)"
-	depends on PM_WAKEUP_GPIO_BY_SIC_IWR
-	default 0x8 if (BF537 || BF536 || BF534)
-	default 0x80 if (BF533 || BF532 || BF531)
-	default 0x80 if (BF54x)
-	default 0x80 if (BF52x)
+config PM_WAKEUP_BY_GPIO
+	bool "Cause Wakeup Event by GPIO"
 
 config PM_WAKEUP_GPIO_NUMBER
 	int "Wakeup GPIO number"
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c
index 6bbe0a2..08788f7 100644
--- a/arch/blackfin/kernel/bfin_gpio.c
+++ b/arch/blackfin/kernel/bfin_gpio.c
@@ -186,7 +186,7 @@
 	char name[RESOURCE_LABEL_SIZE];
 } str_ident[MAX_RESOURCES];
 
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM) && !defined(CONFIG_BF54x)
 static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)];
 static unsigned char wakeup_flags_map[MAX_BLACKFIN_GPIOS];
 static struct gpio_port_s gpio_bank_saved[gpio_bank(MAX_BLACKFIN_GPIOS)];
@@ -696,9 +696,8 @@
 	return 0;
 }
 
-u32 gpio_pm_setup(void)
+u32 bfin_pm_setup(void)
 {
-	u32 sic_iwr = 0;
 	u16 bank, mask, i, gpio;
 
 	for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) {
@@ -723,7 +722,8 @@
 			gpio = i;
 
 			while (mask) {
-				if (mask & 1) {
+				if ((mask & 1) && (wakeup_flags_map[gpio] !=
+					PM_WAKE_IGNORE)) {
 					reserved_gpio_map[gpio_bank(gpio)] |=
 							gpio_bit(gpio);
 					bfin_gpio_wakeup_type(gpio,
@@ -734,21 +734,17 @@
 				mask >>= 1;
 			}
 
-			sic_iwr |= 1 <<
-				(sic_iwr_irqs[bank] - (IRQ_CORETMR + 1));
+			bfin_internal_set_wake(sic_iwr_irqs[bank], 1);
 			gpio_bankb[bank]->maskb_set = wakeup_map[gpio_bank(i)];
 		}
 	}
 
 	AWA_DUMMY_READ(maskb_set);
 
-	if (sic_iwr)
-		return sic_iwr;
-	else
-		return IWR_ENABLE_ALL;
+	return 0;
 }
 
-void gpio_pm_restore(void)
+void bfin_pm_restore(void)
 {
 	u16 bank, mask, i;
 
@@ -768,7 +764,7 @@
 
 			reserved_gpio_map[bank] =
 					gpio_bank_saved[bank].reserved;
-
+			bfin_internal_set_wake(sic_iwr_irqs[bank], 0);
 		}
 
 		gpio_bankb[bank]->maskb = gpio_bank_saved[bank].maskb;
diff --git a/arch/blackfin/mach-common/dpmc.S b/arch/blackfin/mach-common/dpmc.S
index b82c096e..b80ddd8 100644
--- a/arch/blackfin/mach-common/dpmc.S
+++ b/arch/blackfin/mach-common/dpmc.S
@@ -191,6 +191,9 @@
 	call _test_pll_locked;
 
 	R0 = IWR_ENABLE(0);
+	R1 = IWR_DISABLE_ALL;
+	R2 = IWR_DISABLE_ALL;
+
 	call _set_sic_iwr;
 
 	P0.H = hi(PLL_CTL);
@@ -237,6 +240,10 @@
 
 	CLI R4;
 
+	R0 = IWR_ENABLE(0);
+	R1 = IWR_DISABLE_ALL;
+	R2 = IWR_DISABLE_ALL;
+
 	call _set_sic_iwr;
 
 	call _set_dram_srfs;
@@ -261,6 +268,9 @@
 	call _test_pll_locked;
 
 	R0 = IWR_ENABLE(0);
+	R1 = IWR_DISABLE_ALL;
+	R2 = IWR_DISABLE_ALL;
+
 	call _set_sic_iwr;
 
 	P0.H = hi(PLL_CTL);
@@ -286,7 +296,13 @@
 	CLI R4;
 
 	P3 = R0;
+	P4 = R1;
+	P5 = R2;
+
 	R0 = IWR_ENABLE(0);
+	R1 = IWR_DISABLE_ALL;
+	R2 = IWR_DISABLE_ALL;
+
 	call _set_sic_iwr;
 	call _set_dram_srfs;	/* Set SDRAM Self Refresh */
 
@@ -327,6 +343,8 @@
 	call _test_pll_locked;
 
 	R0 = P3;
+	R1 = P4;
+	R3 = P5;
 	call _set_sic_iwr;	/* Set Awake from IDLE */
 
 	P0.H = hi(PLL_CTL);
@@ -340,6 +358,9 @@
 	call _test_pll_locked;
 
 	R0 = IWR_ENABLE(0);
+	R1 = IWR_DISABLE_ALL;
+	R2 = IWR_DISABLE_ALL;
+
 	call _set_sic_iwr;	/* Set Awake from IDLE PLL */
 
 	P0.H = hi(VR_CTL);
@@ -417,14 +438,23 @@
 	RTS;
 
 ENTRY(_set_sic_iwr)
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)  || defined(CONFIG_BF561)
 	P0.H = hi(SIC_IWR0);
 	P0.L = lo(SIC_IWR0);
+	P1.H = hi(SIC_IWR1);
+	P1.L = lo(SIC_IWR1);
+	[P1] = R1;
+#if defined(CONFIG_BF54x)
+	P1.H = hi(SIC_IWR2);
+	P1.L = lo(SIC_IWR2);
+	[P1] = R2;
+#endif
 #else
 	P0.H = hi(SIC_IWR);
 	P0.L = lo(SIC_IWR);
 #endif
 	[P0] = R0;
+
 	SSYNC;
 	RTS;
 
diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c
index 166dbba..81d0018 100644
--- a/arch/blackfin/mach-common/ints-priority.c
+++ b/arch/blackfin/mach-common/ints-priority.c
@@ -1,5 +1,5 @@
 /*
- * File:         arch/blackfin/mach-common/ints-priority-sc.c
+ * File:         arch/blackfin/mach-common/ints-priority.c
  * Based on:
  * Author:
  *
@@ -13,7 +13,7 @@
  *               2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca>
  *               2003 Metrowerks/Motorola
  *               2003 Bas Vermeulen <bas@buyways.nl>
- *               Copyright 2004-2007 Analog Devices Inc.
+ *               Copyright 2004-2008 Analog Devices Inc.
  *
  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
  *
@@ -69,6 +69,10 @@
 /* The number of spurious interrupts */
 atomic_t num_spurious;
 
+#ifdef CONFIG_PM
+unsigned long bfin_sic_iwr[3];	/* Up to 3 SIC_IWRx registers */
+#endif
+
 struct ivgx {
 	/* irq number for request_irq, available in mach-bf533/irq.h */
 	unsigned int irqno;
@@ -178,6 +182,27 @@
 	SSYNC();
 }
 
+#ifdef CONFIG_PM
+int bfin_internal_set_wake(unsigned int irq, unsigned int state)
+{
+	unsigned bank, bit;
+	unsigned long flags;
+	bank = (irq - (IRQ_CORETMR + 1)) / 32;
+	bit = (irq - (IRQ_CORETMR + 1)) % 32;
+
+	local_irq_save(flags);
+
+	if (state)
+		bfin_sic_iwr[bank] |= (1 << bit);
+	else
+		bfin_sic_iwr[bank] &= ~(1 << bit);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+#endif
+
 static struct irq_chip bfin_core_irqchip = {
 	.ack = ack_noop,
 	.mask = bfin_core_mask_irq,
@@ -188,6 +213,9 @@
 	.ack = ack_noop,
 	.mask = bfin_internal_mask_irq,
 	.unmask = bfin_internal_unmask_irq,
+#ifdef CONFIG_PM
+	.set_wake = bfin_internal_set_wake,
+#endif
 };
 
 #ifdef BF537_GENERIC_ERROR_INT_DEMUX
@@ -434,6 +462,20 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+int bfin_gpio_set_wake(unsigned int irq, unsigned int state)
+{
+	unsigned gpio = irq_to_gpio(irq);
+
+	if (state)
+		gpio_pm_wakeup_request(gpio, PM_WAKE_IGNORE);
+	else
+		gpio_pm_wakeup_free(gpio);
+
+	return 0;
+}
+#endif
+
 static struct irq_chip bfin_gpio_irqchip = {
 	.ack = bfin_gpio_ack_irq,
 	.mask = bfin_gpio_mask_irq,
@@ -441,7 +483,10 @@
 	.unmask = bfin_gpio_unmask_irq,
 	.set_type = bfin_gpio_irq_type,
 	.startup = bfin_gpio_irq_startup,
-	.shutdown = bfin_gpio_irq_shutdown
+	.shutdown = bfin_gpio_irq_shutdown,
+#ifdef CONFIG_PM
+	.set_wake = bfin_gpio_set_wake,
+#endif
 };
 
 static void bfin_demux_gpio_irq(unsigned int inta_irq,
@@ -487,7 +532,7 @@
 	}
 
 	if (search) {
-		for (i = 0; i < MAX_BLACKFIN_GPIOS; i += 16) {
+		for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) {
 			irq += i;
 
 			mask = get_gpiop_data(i) &
@@ -763,6 +808,74 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+u32 pint_saved_masks[NR_PINT_SYS_IRQS];
+u32 pint_wakeup_masks[NR_PINT_SYS_IRQS];
+
+int bfin_gpio_set_wake(unsigned int irq, unsigned int state)
+{
+	u32 pint_irq;
+	u8 pint_val = irq2pint_lut[irq - SYS_IRQS];
+	u32 bank = PINT_2_BANK(pint_val);
+	u32 pintbit = PINT_BIT(pint_val);
+
+	switch (bank) {
+	case 0:
+		pint_irq = IRQ_PINT0;
+		break;
+	case 2:
+		pint_irq = IRQ_PINT2;
+		break;
+	case 3:
+		pint_irq = IRQ_PINT3;
+		break;
+	case 1:
+		pint_irq = IRQ_PINT1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	bfin_internal_set_wake(pint_irq, state);
+
+	if (state)
+		pint_wakeup_masks[bank] |= pintbit;
+	else
+		pint_wakeup_masks[bank] &= ~pintbit;
+
+	return 0;
+}
+
+u32 bfin_pm_setup(void)
+{
+	u32 val, i;
+
+	for (i = 0; i < NR_PINT_SYS_IRQS; i++) {
+		val = pint[i]->mask_clear;
+		pint_saved_masks[i] = val;
+		if (val ^ pint_wakeup_masks[i]) {
+			pint[i]->mask_clear = val;
+			pint[i]->mask_set = pint_wakeup_masks[i];
+		}
+	}
+
+	return 0;
+}
+
+void bfin_pm_restore(void)
+{
+	u32 i, val;
+
+	for (i = 0; i < NR_PINT_SYS_IRQS; i++) {
+		val = pint_saved_masks[i];
+		if (val ^ pint_wakeup_masks[i]) {
+			pint[i]->mask_clear = pint[i]->mask_clear;
+			pint[i]->mask_set = val;
+		}
+	}
+}
+#endif
+
 static struct irq_chip bfin_gpio_irqchip = {
 	.ack = bfin_gpio_ack_irq,
 	.mask = bfin_gpio_mask_irq,
@@ -770,7 +883,10 @@
 	.unmask = bfin_gpio_unmask_irq,
 	.set_type = bfin_gpio_irq_type,
 	.startup = bfin_gpio_irq_startup,
-	.shutdown = bfin_gpio_irq_shutdown
+	.shutdown = bfin_gpio_irq_shutdown,
+#ifdef CONFIG_PM
+	.set_wake = bfin_gpio_set_wake,
+#endif
 };
 
 static void bfin_demux_gpio_irq(unsigned int inta_irq,
diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c
index 81930f7..0be805c 100644
--- a/arch/blackfin/mach-common/pm.c
+++ b/arch/blackfin/mach-common/pm.c
@@ -4,7 +4,7 @@
  * Author:       Cliff Brake <cbrake@accelent.com> Copyright (c) 2001
  *
  * Created:      2001
- * Description:  Power management for the bfin
+ * Description:  Blackfin power management
  *
  * Modified:     Nicolas Pitre - PXA250 support
  *                Copyright (c) 2002 Monta Vista Software, Inc.
@@ -12,7 +12,7 @@
  *                Copyright (c) 2002 Monta Vista Software, Inc.
  *               Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610
  *                Copyright 2004
- *               Copyright 2004-2006 Analog Devices Inc.
+ *               Copyright 2004-2008 Analog Devices Inc.
  *
  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
  *
@@ -67,42 +67,30 @@
 	gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE);
 #endif
 
-#if defined(CONFIG_PM_WAKEUP_BY_GPIO) || defined(CONFIG_PM_WAKEUP_GPIO_API)
-	{
-		u32 flags;
+	u32 flags;
 
-		local_irq_save(flags);
+	local_irq_save(flags);
+	bfin_pm_setup();
 
-		sleep_deeper(gpio_pm_setup()); /*Goto Sleep*/
-
-		gpio_pm_restore();
-
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)
-		bfin_write_SIC_IWR0(IWR_ENABLE_ALL);
-		bfin_write_SIC_IWR1(IWR_ENABLE_ALL);
-# ifdef CONFIG_BF54x
-		bfin_write_SIC_IWR2(IWR_ENABLE_ALL);
-# endif
+#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER
+	sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
 #else
-		bfin_write_SIC_IWR(IWR_ENABLE_ALL);
+	sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
 #endif
 
-		local_irq_restore(flags);
-	}
-#endif
+	bfin_pm_restore();
 
-#if defined(CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR)
-	sleep_deeper(CONFIG_PM_WAKEUP_SIC_IWR);
-# if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)  || defined(CONFIG_BF561)
 	bfin_write_SIC_IWR0(IWR_ENABLE_ALL);
 	bfin_write_SIC_IWR1(IWR_ENABLE_ALL);
-#  ifdef CONFIG_BF54x
+# ifdef CONFIG_BF54x
 	bfin_write_SIC_IWR2(IWR_ENABLE_ALL);
-#  endif
-# else
-	bfin_write_SIC_IWR(IWR_ENABLE_ALL);
 # endif
-#endif				/* CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR */
+#else
+	bfin_write_SIC_IWR(IWR_ENABLE_ALL);
+#endif
+
+	local_irq_restore(flags);
 }
 
 /*
diff --git a/include/asm-blackfin/bfin-global.h b/include/asm-blackfin/bfin-global.h
index 6ae0619..5dba3a7 100644
--- a/include/asm-blackfin/bfin-global.h
+++ b/include/asm-blackfin/bfin-global.h
@@ -70,6 +70,7 @@
 extern void evt14_softirq(void);
 extern asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs);
 extern void bfin_gpio_interrupt_setup(int irq, int irq_pfx, int type);
+extern int bfin_internal_set_wake(unsigned int irq, unsigned int state);
 
 extern asmlinkage void finish_atomic_sections (struct pt_regs *regs);
 extern char fixed_code_start;
@@ -121,6 +122,7 @@
 
 extern unsigned long table_start, table_end;
 
+extern unsigned long bfin_sic_iwr[];
 extern u16 _bfin_swrst; /* shadow for Software Reset Register (SWRST) */
 extern struct file_operations dpmc_fops;
 extern char _start;
diff --git a/include/asm-blackfin/dpmc.h b/include/asm-blackfin/dpmc.h
index f162edb..686cf83 100644
--- a/include/asm-blackfin/dpmc.h
+++ b/include/asm-blackfin/dpmc.h
@@ -53,10 +53,10 @@
 void change_baud(int baud);
 void fullon_mode(void);
 void active_mode(void);
-void sleep_mode(u32 sic_iwr);
-void deep_sleep(u32 sic_iwr);
-void hibernate_mode(u32 sic_iwr);
-void sleep_deeper(u32 sic_iwr);
+void sleep_mode(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
+void deep_sleep(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
+void hibernate_mode(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
+void sleep_deeper(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
 void program_wdog_timer(unsigned long);
 void unmask_wdog_wakeup_evt(void);
 void clear_wdog_wakeup_evt(void);
diff --git a/include/asm-blackfin/gpio.h b/include/asm-blackfin/gpio.h
index d0426c1..27ff532 100644
--- a/include/asm-blackfin/gpio.h
+++ b/include/asm-blackfin/gpio.h
@@ -376,16 +376,19 @@
 #endif
 
 #ifdef CONFIG_PM
+unsigned int bfin_pm_setup(void);
+void bfin_pm_restore(void);
+
+#ifndef CONFIG_BF54x
 #define PM_WAKE_RISING	0x1
 #define PM_WAKE_FALLING	0x2
 #define PM_WAKE_HIGH	0x4
 #define PM_WAKE_LOW	0x8
 #define PM_WAKE_BOTH_EDGES	(PM_WAKE_RISING | PM_WAKE_FALLING)
+#define PM_WAKE_IGNORE	0xF0
 
 int gpio_pm_wakeup_request(unsigned gpio, unsigned char type);
 void gpio_pm_wakeup_free(unsigned gpio);
-unsigned int gpio_pm_setup(void);
-void gpio_pm_restore(void);
 
 struct gpio_port_s {
 	unsigned short data;
@@ -409,6 +412,7 @@
 	unsigned short fer;
 	unsigned short reserved;
 };
+#endif /*CONFIG_BF54x*/
 #endif /*CONFIG_PM*/
 
 /***********************************************************