From: Haavard Skinnemoen <[email protected]>
The following series fixes a few drivers in order to make power
management behave more nicely on AVR32. It then goes on to implement
support for the "standby" and "mem" suspend modes on the AT32AP7000.
If it's ok with everyone, I'll merge all the bits that don't get
picked up by someone else for 2.6.27.
The patches are meant to go on top of the avr32-arch branch of
git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen/avr32-2.6.git
but they should apply nicely to the -next and -mm trees too.
Haavard Skinnemoen (8):
atmel_serial: Fix build on avr32 with CONFIG_PM enabled
atmel_serial: Drain console TX shifter before suspending
macb: Basic suspend/resume support
rtc-at32ap700x: Enable wakeup
avr32: Enable SDRAMC clock at startup
avr32: Add simple SRAM allocator
avr32: Add system device for the internal interrupt controller (intc)
avr32: Power Management support ("standby" and "mem" modes)
arch/avr32/Kconfig | 6 +
arch/avr32/mach-at32ap/Makefile | 5 +
arch/avr32/mach-at32ap/at32ap700x.c | 35 +++++
arch/avr32/mach-at32ap/intc.c | 80 ++++++++++-
arch/avr32/mach-at32ap/pm-at32ap700x.S | 108 ++++++++++++++
arch/avr32/mach-at32ap/pm.c | 245 ++++++++++++++++++++++++++++++++
arch/avr32/mach-at32ap/sdramc.h | 76 ++++++++++
drivers/net/macb.c | 37 +++++
drivers/rtc/rtc-at32ap700x.c | 3 +
drivers/serial/atmel_serial.c | 17 ++-
include/asm-avr32/arch-at32ap/pm.h | 3 +
include/asm-avr32/arch-at32ap/sram.h | 30 ++++
include/asm-avr32/thread_info.h | 1 +
13 files changed, 642 insertions(+), 4 deletions(-)
create mode 100644 arch/avr32/mach-at32ap/pm.c
create mode 100644 arch/avr32/mach-at32ap/sdramc.h
create mode 100644 include/asm-avr32/arch-at32ap/sram.h
Haavard
From: Haavard Skinnemoen <[email protected]>
AVR32 doesn't have at91_suspend_entering_slow_clock(). Just assume the
clock will keep running for now.
David has a better solution for this, but this works for now. Leaving
the USART clock running won't prevent the PM code from entering deep
power-down modes anyway.
Signed-off-by: Haavard Skinnemoen <[email protected]>
Cc: David Brownell <[email protected]>
Cc: Andrew Victor <[email protected]>
---
drivers/serial/atmel_serial.c | 11 ++++++++++-
1 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 42be8b0..5f0414f 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -1439,6 +1439,15 @@ static struct uart_driver atmel_uart = {
};
#ifdef CONFIG_PM
+static bool atmel_serial_clk_will_stop(void)
+{
+#ifdef CONFIG_ARCH_AT91
+ return at91_suspend_entering_slow_clock();
+#else
+ return false;
+#endif
+}
+
static int atmel_serial_suspend(struct platform_device *pdev,
pm_message_t state)
{
@@ -1446,7 +1455,7 @@ static int atmel_serial_suspend(struct platform_device *pdev,
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
if (device_may_wakeup(&pdev->dev)
- && !at91_suspend_entering_slow_clock())
+ && !atmel_serial_clk_will_stop())
enable_irq_wake(port->irq);
else {
uart_suspend_port(&atmel_uart, port);
--
1.5.5.4
Call device_init_wakeup() to signal that the RTC is capable of waking
the system. This is needed for rtcwake to work.
Signed-off-by: Haavard Skinnemoen <[email protected]>
Cc: Hans-Christian Egtvedt <[email protected]>
Cc: Alessandro Zummo <[email protected]>
---
drivers/rtc/rtc-at32ap700x.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index 2ef8cdf..90b9a65 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -265,6 +265,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, rtc);
+ device_init_wakeup(&pdev->dev, 1);
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
(unsigned long)rtc->regs, rtc->irq);
@@ -284,6 +285,8 @@ static int __exit at32_rtc_remove(struct platform_device *pdev)
{
struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
+ device_init_wakeup(&pdev->dev, 0);
+
free_irq(rtc->irq, rtc);
iounmap(rtc->regs);
rtc_device_unregister(rtc->rtc);
--
1.5.5.4
From: Haavard Skinnemoen <[email protected]>
This makes the intc show up in sysfs (probably not very useful), and
allows us to easily add suspend/resume support later.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
arch/avr32/mach-at32ap/intc.c | 28 +++++++++++++++++++++++++---
1 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
index 097cf4e..644a3fb 100644
--- a/arch/avr32/mach-at32ap/intc.c
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Atmel Corporation
+ * Copyright (C) 2006, 2008 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -12,14 +12,16 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/sysdev.h>
#include <asm/io.h>
#include "intc.h"
struct intc {
- void __iomem *regs;
- struct irq_chip chip;
+ void __iomem *regs;
+ struct irq_chip chip;
+ struct sys_device sysdev;
};
extern struct platform_device at32_intc0_device;
@@ -136,6 +138,26 @@ fail:
panic("Interrupt controller initialization failed!\n");
}
+static struct sysdev_class intc_class = {
+ .name = "intc",
+};
+
+static int __init intc_init_sysdev(void)
+{
+ int ret;
+
+ ret = sysdev_class_register(&intc_class);
+ if (ret)
+ return ret;
+
+ intc0.sysdev.id = 0;
+ intc0.sysdev.cls = &intc_class;
+ ret = sysdev_register(&intc0.sysdev);
+
+ return ret;
+}
+device_initcall(intc_init_sysdev);
+
unsigned long intc_get_pending(unsigned int group)
{
return intc_readl(&intc0, INTREQ0 + 4 * group);
--
1.5.5.4
Funny things may happen if we stop the USART clock before the shifter is
empty. Prevent this from happening by waiting until the shifter is
completely drained before allowing suspend to continue.
Signed-off-by: Haavard Skinnemoen <[email protected]>
Cc: Andrew Victor <[email protected]>
---
drivers/serial/atmel_serial.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 5f0414f..6aeef22 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -1454,6 +1454,12 @@ static int atmel_serial_suspend(struct platform_device *pdev,
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ if (atmel_is_console_port(port) && console_suspend_enabled) {
+ /* Drain the TX shifter */
+ while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
+ cpu_relax();
+ }
+
if (device_may_wakeup(&pdev->dev)
&& !atmel_serial_clk_will_stop())
enable_irq_wake(port->irq);
--
1.5.5.4
From: Haavard Skinnemoen <[email protected]>
The SDRAM controller needs a clock in order to respond to our
commands, and suspend doesn't work very well without the SDRAM in
self-refresh mode.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
arch/avr32/mach-at32ap/at32ap700x.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index baf4680..3ee5e72 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -675,6 +675,14 @@ static struct clk hramc_clk = {
.users = 1,
.index = 3,
};
+static struct clk sdramc_clk = {
+ .name = "sdramc_clk",
+ .parent = &pbb_clk,
+ .mode = pbb_clk_mode,
+ .get_rate = pbb_clk_get_rate,
+ .users = 1,
+ .index = 14,
+};
static struct resource smc0_resource[] = {
PBMEM(0xfff03400),
@@ -1998,6 +2006,7 @@ struct clk *at32_clock_list[] = {
&hmatrix_clk,
&ebi_clk,
&hramc_clk,
+ &sdramc_clk,
&smc0_pclk,
&smc0_mck,
&pdc_hclk,
--
1.5.5.4
From: Haavard Skinnemoen <[email protected]>
Implement Standby support. In this mode, we'll suspend all drivers,
put the SDRAM in self-refresh mode and switch off the HSB bus
("frozen" mode.)
Implement Suspend-to-mem support. In this mode, we suspend all
drivers, put the SDRAM into self-refresh mode and switch off all
internal clocks except the 32 kHz oscillator ("stop" mode.)
The lowest-level suspend code runs from a small portion of SRAM
allocated at startup time. This gets rid of a small potential race
with the SDRAM where we might try to enter self-refresh mode in the
middle of an icache burst. We also relocate all interrupt and
exception handlers to SRAM during the small window when we enter and
exit the low-power modes.
We don't need to do any special tricks to start and stop the PLL. The
main clock is automatically gated by hardware until the PLL is stable.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
arch/avr32/Kconfig | 5 +
arch/avr32/mach-at32ap/Makefile | 5 +
arch/avr32/mach-at32ap/intc.c | 54 +++++++-
arch/avr32/mach-at32ap/pm-at32ap700x.S | 108 ++++++++++++++
arch/avr32/mach-at32ap/pm.c | 245 ++++++++++++++++++++++++++++++++
arch/avr32/mach-at32ap/sdramc.h | 76 ++++++++++
include/asm-avr32/arch-at32ap/pm.h | 3 +
include/asm-avr32/thread_info.h | 1 +
8 files changed, 496 insertions(+), 1 deletions(-)
create mode 100644 arch/avr32/mach-at32ap/pm.c
create mode 100644 arch/avr32/mach-at32ap/sdramc.h
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index 4ff3dea..da86525 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -202,6 +202,11 @@ endmenu
menu "Power management options"
+source "kernel/power/Kconfig"
+
+config ARCH_SUSPEND_POSSIBLE
+ def_bool y
+
menu "CPU Frequency scaling"
source "drivers/cpufreq/Kconfig"
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index cb44182..d5018e2 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,3 +1,8 @@
obj-y += pdc.o clock.o intc.o extint.o pio.o hsmc.o
obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o
obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o
+obj-$(CONFIG_PM) += pm.o
+
+ifeq ($(CONFIG_PM_DEBUG),y)
+CFLAGS_pm.o += -DDEBUG
+endif
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
index 644a3fb..994c454 100644
--- a/arch/avr32/mach-at32ap/intc.c
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -22,6 +22,10 @@ struct intc {
void __iomem *regs;
struct irq_chip chip;
struct sys_device sysdev;
+#ifdef CONFIG_PM
+ unsigned long suspend_ipr;
+ unsigned long saved_ipr[64];
+#endif
};
extern struct platform_device at32_intc0_device;
@@ -138,8 +142,56 @@ fail:
panic("Interrupt controller initialization failed!\n");
}
+#ifdef CONFIG_PM
+void intc_set_suspend_handler(unsigned long offset)
+{
+ intc0.suspend_ipr = offset;
+}
+
+static int intc_suspend(struct sys_device *sdev, pm_message_t state)
+{
+ struct intc *intc = container_of(sdev, struct intc, sysdev);
+ int i;
+
+ if (unlikely(!irqs_disabled())) {
+ pr_err("intc_suspend: called with interrupts enabled\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(!intc->suspend_ipr)) {
+ pr_err("intc_suspend: suspend_ipr not initialized\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 64; i++) {
+ intc->saved_ipr[i] = intc_readl(intc, INTPR0 + 4 * i);
+ intc_writel(intc, INTPR0 + 4 * i, intc->suspend_ipr);
+ }
+
+ return 0;
+}
+
+static int intc_resume(struct sys_device *sdev)
+{
+ struct intc *intc = container_of(sdev, struct intc, sysdev);
+ int i;
+
+ WARN_ON(!irqs_disabled());
+
+ for (i = 0; i < 64; i++)
+ intc_writel(intc, INTPR0 + 4 * i, intc->saved_ipr[i]);
+
+ return 0;
+}
+#else
+#define intc_suspend NULL
+#define intc_resume NULL
+#endif
+
static struct sysdev_class intc_class = {
- .name = "intc",
+ .name = "intc",
+ .suspend = intc_suspend,
+ .resume = intc_resume,
};
static int __init intc_init_sysdev(void)
diff --git a/arch/avr32/mach-at32ap/pm-at32ap700x.S b/arch/avr32/mach-at32ap/pm-at32ap700x.S
index 949e248..0a53ad3 100644
--- a/arch/avr32/mach-at32ap/pm-at32ap700x.S
+++ b/arch/avr32/mach-at32ap/pm-at32ap700x.S
@@ -12,6 +12,12 @@
#include <asm/thread_info.h>
#include <asm/arch/pm.h>
+#include "pm.h"
+#include "sdramc.h"
+
+/* Same as 0xfff00000 but fits in a 21 bit signed immediate */
+#define PM_BASE -0x100000
+
.section .bss, "wa", @nobits
.global disable_idle_sleep
.type disable_idle_sleep, @object
@@ -64,3 +70,105 @@ cpu_idle_skip_sleep:
unmask_interrupts
retal r12
.size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep
+
+#ifdef CONFIG_PM
+ .section .init.text, "ax", @progbits
+
+ .global pm_exception
+ .type pm_exception, @function
+pm_exception:
+ /*
+ * Exceptions are masked when we switch to this handler, so
+ * we'll only get "unrecoverable" exceptions (offset 0.)
+ */
+ sub r12, pc, . - .Lpanic_msg
+ lddpc pc, .Lpanic_addr
+
+ .align 2
+.Lpanic_addr:
+ .long panic
+.Lpanic_msg:
+ .asciz "Unrecoverable exception during suspend\n"
+ .size pm_exception, . - pm_exception
+
+ .global pm_irq0
+ .type pm_irq0, @function
+pm_irq0:
+ /* Disable interrupts and return after the sleep instruction */
+ mfsr r9, SYSREG_RSR_INT0
+ mtsr SYSREG_RAR_INT0, r8
+ sbr r9, SYSREG_GM_OFFSET
+ mtsr SYSREG_RSR_INT0, r9
+ rete
+
+ /*
+ * void cpu_enter_standby(unsigned long sdramc_base)
+ *
+ * Enter PM_SUSPEND_STANDBY mode. At this point, all drivers
+ * are suspended and interrupts are disabled. Interrupts
+ * marked as 'wakeup' event sources may still come along and
+ * get us out of here.
+ *
+ * The SDRAM will be put into self-refresh mode (which does
+ * not require a clock from the CPU), and the CPU will be put
+ * into "frozen" mode (HSB bus stopped). The SDRAM controller
+ * will automatically bring the SDRAM into normal mode on the
+ * first access, and the power manager will automatically
+ * start the HSB and CPU clocks upon a wakeup event.
+ *
+ * This code uses the same "skip sleep" technique as above.
+ * It is very important that we jump directly to
+ * cpu_after_sleep after the sleep instruction since that's
+ * where we'll end up if the interrupt handler decides that we
+ * need to skip the sleep instruction.
+ */
+ .global pm_standby
+ .type pm_standby, @function
+pm_standby:
+ /*
+ * interrupts are already masked at this point, and EVBA
+ * points to pm_exception above.
+ */
+ ld.w r10, r12[SDRAMC_LPR]
+ sub r8, pc, . - 1f /* return address for irq handler */
+ mov r11, SDRAMC_LPR_LPCB_SELF_RFR
+ bfins r10, r11, 0, 2 /* LPCB <- self Refresh */
+ sync 0 /* flush write buffer */
+ st.w r12[SDRAMC_LPR], r11 /* put SDRAM in self-refresh mode */
+ ld.w r11, r12[SDRAMC_LPR]
+ unmask_interrupts
+ sleep CPU_SLEEP_FROZEN
+1: mask_interrupts
+ retal r12
+ .size pm_standby, . - pm_standby
+
+ .global pm_suspend_to_ram
+ .type pm_suspend_to_ram, @function
+pm_suspend_to_ram:
+ /*
+ * interrupts are already masked at this point, and EVBA
+ * points to pm_exception above.
+ */
+ mov r11, 0
+ cache r11[2], 8 /* clean all dcache lines */
+ sync 0 /* flush write buffer */
+ ld.w r10, r12[SDRAMC_LPR]
+ sub r8, pc, . - 1f /* return address for irq handler */
+ mov r11, SDRAMC_LPR_LPCB_SELF_RFR
+ bfins r10, r11, 0, 2 /* LPCB <- self refresh */
+ st.w r12[SDRAMC_LPR], r10 /* put SDRAM in self-refresh mode */
+ ld.w r11, r12[SDRAMC_LPR]
+
+ unmask_interrupts
+ sleep CPU_SLEEP_STOP
+1: mask_interrupts
+
+ retal r12
+ .size pm_suspend_to_ram, . - pm_suspend_to_ram
+
+ .global pm_sram_end
+ .type pm_sram_end, @function
+pm_sram_end:
+ .size pm_sram_end, 0
+
+#endif /* CONFIG_PM */
diff --git a/arch/avr32/mach-at32ap/pm.c b/arch/avr32/mach-at32ap/pm.c
new file mode 100644
index 0000000..0b76432
--- /dev/null
+++ b/arch/avr32/mach-at32ap/pm.c
@@ -0,0 +1,245 @@
+/*
+ * AVR32 AP Power Management
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+#include <linux/io.h>
+#include <linux/suspend.h>
+#include <linux/vmalloc.h>
+
+#include <asm/cacheflush.h>
+#include <asm/sysreg.h>
+
+#include <asm/arch/pm.h>
+#include <asm/arch/sram.h>
+
+/* FIXME: This is only valid for AP7000 */
+#define SDRAMC_BASE 0xfff03800
+
+#include "sdramc.h"
+
+#define SRAM_PAGE_FLAGS (SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1) \
+ | SYSREG_BF(AP, 3) | SYSREG_BIT(G))
+
+
+static unsigned long pm_sram_start;
+static size_t pm_sram_size;
+static struct vm_struct *pm_sram_area;
+
+static void (*avr32_pm_enter_standby)(unsigned long sdramc_base);
+static void (*avr32_pm_enter_str)(unsigned long sdramc_base);
+
+/*
+ * Must be called with interrupts disabled. Exceptions will be masked
+ * on return (i.e. all exceptions will be "unrecoverable".)
+ */
+static void *avr32_pm_map_sram(void)
+{
+ unsigned long vaddr;
+ unsigned long page_addr;
+ u32 tlbehi;
+ u32 mmucr;
+
+ vaddr = (unsigned long)pm_sram_area->addr;
+ page_addr = pm_sram_start & PAGE_MASK;
+
+ /*
+ * Mask exceptions and grab the first TLB entry. We won't be
+ * needing it while sleeping.
+ */
+ asm volatile("ssrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
+
+ mmucr = sysreg_read(MMUCR);
+ tlbehi = sysreg_read(TLBEHI);
+ sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
+
+ tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
+ tlbehi |= vaddr & PAGE_MASK;
+ tlbehi |= SYSREG_BIT(TLBEHI_V);
+
+ sysreg_write(TLBELO, page_addr | SRAM_PAGE_FLAGS);
+ sysreg_write(TLBEHI, tlbehi);
+ __builtin_tlbw();
+
+ return (void *)(vaddr + pm_sram_start - page_addr);
+}
+
+/*
+ * Must be called with interrupts disabled. Exceptions will be
+ * unmasked on return.
+ */
+static void avr32_pm_unmap_sram(void)
+{
+ u32 mmucr;
+ u32 tlbehi;
+ u32 tlbarlo;
+
+ /* Going to update TLB entry at index 0 */
+ mmucr = sysreg_read(MMUCR);
+ tlbehi = sysreg_read(TLBEHI);
+ sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
+
+ /* Clear the "valid" bit */
+ tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
+ sysreg_write(TLBEHI, tlbehi);
+
+ /* Mark it as "not accessed" */
+ tlbarlo = sysreg_read(TLBARLO);
+ sysreg_write(TLBARLO, tlbarlo | 0x80000000U);
+
+ /* Update the TLB */
+ __builtin_tlbw();
+
+ /* Unmask exceptions */
+ asm volatile("csrf %0" : : "i"(SYSREG_EM_OFFSET) : "memory");
+}
+
+static int avr32_pm_valid_state(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static int avr32_pm_enter(suspend_state_t state)
+{
+ u32 lpr_saved;
+ u32 evba_saved;
+ void *sram;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ sram = avr32_pm_map_sram();
+
+ /* Switch to in-sram exception handlers */
+ evba_saved = sysreg_read(EVBA);
+ sysreg_write(EVBA, (unsigned long)sram);
+
+ /*
+ * Save the LPR register so that we can re-enable
+ * SDRAM Low Power mode on resume.
+ */
+ lpr_saved = sdramc_readl(LPR);
+ pr_debug("%s: Entering standby...\n", __func__);
+ avr32_pm_enter_standby(SDRAMC_BASE);
+ sdramc_writel(LPR, lpr_saved);
+
+ /* Switch back to regular exception handlers */
+ sysreg_write(EVBA, evba_saved);
+
+ avr32_pm_unmap_sram();
+ break;
+
+ case PM_SUSPEND_MEM:
+ sram = avr32_pm_map_sram();
+
+ /* Switch to in-sram exception handlers */
+ evba_saved = sysreg_read(EVBA);
+ sysreg_write(EVBA, (unsigned long)sram);
+
+ /*
+ * Save the LPR register so that we can re-enable
+ * SDRAM Low Power mode on resume.
+ */
+ lpr_saved = sdramc_readl(LPR);
+ pr_debug("%s: Entering suspend-to-ram...\n", __func__);
+ avr32_pm_enter_str(SDRAMC_BASE);
+ sdramc_writel(LPR, lpr_saved);
+
+ /* Switch back to regular exception handlers */
+ sysreg_write(EVBA, evba_saved);
+
+ avr32_pm_unmap_sram();
+ break;
+
+ case PM_SUSPEND_ON:
+ pr_debug("%s: Entering idle...\n", __func__);
+ cpu_enter_idle();
+ break;
+
+ default:
+ pr_debug("%s: Invalid suspend state %d\n", __func__, state);
+ goto out;
+ }
+
+ pr_debug("%s: wakeup\n", __func__);
+
+out:
+ return 0;
+}
+
+static struct platform_suspend_ops avr32_pm_ops = {
+ .valid = avr32_pm_valid_state,
+ .enter = avr32_pm_enter,
+};
+
+static unsigned long avr32_pm_offset(void *symbol)
+{
+ extern u8 pm_exception[];
+
+ return (unsigned long)symbol - (unsigned long)pm_exception;
+}
+
+static int __init avr32_pm_init(void)
+{
+ extern u8 pm_exception[];
+ extern u8 pm_irq0[];
+ extern u8 pm_standby[];
+ extern u8 pm_suspend_to_ram[];
+ extern u8 pm_sram_end[];
+ void *dst;
+
+ /*
+ * To keep things simple, we depend on not needing more than a
+ * single page.
+ */
+ pm_sram_size = avr32_pm_offset(pm_sram_end);
+ if (pm_sram_size > PAGE_SIZE)
+ goto err;
+
+ pm_sram_start = sram_alloc(pm_sram_size);
+ if (!pm_sram_start)
+ goto err_alloc_sram;
+
+ /* Grab a virtual area we can use later on. */
+ pm_sram_area = get_vm_area(pm_sram_size, VM_IOREMAP);
+ if (!pm_sram_area)
+ goto err_vm_area;
+ pm_sram_area->phys_addr = pm_sram_start;
+
+ local_irq_disable();
+ dst = avr32_pm_map_sram();
+ memcpy(dst, pm_exception, pm_sram_size);
+ flush_dcache_region(dst, pm_sram_size);
+ invalidate_icache_region(dst, pm_sram_size);
+ avr32_pm_unmap_sram();
+ local_irq_enable();
+
+ avr32_pm_enter_standby = dst + avr32_pm_offset(pm_standby);
+ avr32_pm_enter_str = dst + avr32_pm_offset(pm_suspend_to_ram);
+ intc_set_suspend_handler(avr32_pm_offset(pm_irq0));
+
+ suspend_set_ops(&avr32_pm_ops);
+
+ printk("AVR32 AP Power Management enabled\n");
+
+ return 0;
+
+err_vm_area:
+ sram_free(pm_sram_start, pm_sram_size);
+err_alloc_sram:
+err:
+ pr_err("AVR32 Power Management initialization failed\n");
+ return -ENOMEM;
+}
+arch_initcall(avr32_pm_init);
diff --git a/arch/avr32/mach-at32ap/sdramc.h b/arch/avr32/mach-at32ap/sdramc.h
new file mode 100644
index 0000000..66eeaed
--- /dev/null
+++ b/arch/avr32/mach-at32ap/sdramc.h
@@ -0,0 +1,76 @@
+/*
+ * Register definitions for the AT32AP SDRAM Controller
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/* Register offsets */
+#define SDRAMC_MR 0x0000
+#define SDRAMC_TR 0x0004
+#define SDRAMC_CR 0x0008
+#define SDRAMC_HSR 0x000c
+#define SDRAMC_LPR 0x0010
+#define SDRAMC_IER 0x0014
+#define SDRAMC_IDR 0x0018
+#define SDRAMC_IMR 0x001c
+#define SDRAMC_ISR 0x0020
+#define SDRAMC_MDR 0x0024
+
+/* MR - Mode Register */
+#define SDRAMC_MR_MODE_NORMAL ( 0 << 0)
+#define SDRAMC_MR_MODE_NOP ( 1 << 0)
+#define SDRAMC_MR_MODE_BANKS_PRECHARGE ( 2 << 0)
+#define SDRAMC_MR_MODE_LOAD_MODE ( 3 << 0)
+#define SDRAMC_MR_MODE_AUTO_REFRESH ( 4 << 0)
+#define SDRAMC_MR_MODE_EXT_LOAD_MODE ( 5 << 0)
+#define SDRAMC_MR_MODE_POWER_DOWN ( 6 << 0)
+
+/* CR - Configuration Register */
+#define SDRAMC_CR_NC_8_BITS ( 0 << 0)
+#define SDRAMC_CR_NC_9_BITS ( 1 << 0)
+#define SDRAMC_CR_NC_10_BITS ( 2 << 0)
+#define SDRAMC_CR_NC_11_BITS ( 3 << 0)
+#define SDRAMC_CR_NR_11_BITS ( 0 << 2)
+#define SDRAMC_CR_NR_12_BITS ( 1 << 2)
+#define SDRAMC_CR_NR_13_BITS ( 2 << 2)
+#define SDRAMC_CR_NB_2_BANKS ( 0 << 4)
+#define SDRAMC_CR_NB_4_BANKS ( 1 << 4)
+#define SDRAMC_CR_CAS(x) ((x) << 5)
+#define SDRAMC_CR_DBW_32_BITS ( 0 << 7)
+#define SDRAMC_CR_DBW_16_BITS ( 1 << 7)
+#define SDRAMC_CR_TWR(x) ((x) << 8)
+#define SDRAMC_CR_TRC(x) ((x) << 12)
+#define SDRAMC_CR_TRP(x) ((x) << 16)
+#define SDRAMC_CR_TRCD(x) ((x) << 20)
+#define SDRAMC_CR_TRAS(x) ((x) << 24)
+#define SDRAMC_CR_TXSR(x) ((x) << 28)
+
+/* HSR - High Speed Register */
+#define SDRAMC_HSR_DA ( 1 << 0)
+
+/* LPR - Low Power Register */
+#define SDRAMC_LPR_LPCB_INHIBIT ( 0 << 0)
+#define SDRAMC_LPR_LPCB_SELF_RFR ( 1 << 0)
+#define SDRAMC_LPR_LPCB_PDOWN ( 2 << 0)
+#define SDRAMC_LPR_LPCB_DEEP_PDOWN ( 3 << 0)
+#define SDRAMC_LPR_PASR(x) ((x) << 4)
+#define SDRAMC_LPR_TCSR(x) ((x) << 8)
+#define SDRAMC_LPR_DS(x) ((x) << 10)
+#define SDRAMC_LPR_TIMEOUT(x) ((x) << 12)
+
+/* IER/IDR/IMR/ISR - Interrupt Enable/Disable/Mask/Status Register */
+#define SDRAMC_ISR_RES ( 1 << 0)
+
+/* MDR - Memory Device Register */
+#define SDRAMC_MDR_MD_SDRAM ( 0 << 0)
+#define SDRAMC_MDR_MD_LOW_PWR_SDRAM ( 1 << 0)
+
+/* Register access macros */
+#define sdramc_readl(reg) \
+ __raw_readl((void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)
+#define sdramc_writel(reg, value) \
+ __raw_writel(value, (void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)
diff --git a/include/asm-avr32/arch-at32ap/pm.h b/include/asm-avr32/arch-at32ap/pm.h
index 356e430..979b355 100644
--- a/include/asm-avr32/arch-at32ap/pm.h
+++ b/include/asm-avr32/arch-at32ap/pm.h
@@ -19,6 +19,7 @@
#ifndef __ASSEMBLY__
extern void cpu_enter_idle(void);
+extern void cpu_enter_standby(unsigned long sdramc_base);
extern bool disable_idle_sleep;
@@ -43,6 +44,8 @@ static inline void cpu_idle_sleep(void)
else
cpu_enter_idle();
}
+
+void intc_set_suspend_handler(unsigned long offset);
#endif
#endif /* __ASM_AVR32_ARCH_PM_H */
diff --git a/include/asm-avr32/thread_info.h b/include/asm-avr32/thread_info.h
index 07049f6..df68631 100644
--- a/include/asm-avr32/thread_info.h
+++ b/include/asm-avr32/thread_info.h
@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_MEMDIE 6
#define TIF_RESTORE_SIGMASK 7 /* restore signal mask in do_signal */
#define TIF_CPU_GOING_TO_SLEEP 8 /* CPU is entering sleep 0 mode */
+#define TIF_FREEZE 29
#define TIF_DEBUG 30 /* debugging enabled */
#define TIF_USERSPACE 31 /* true if FS sets userspace */
--
1.5.5.4
From: Haavard Skinnemoen <[email protected]>
This implements suspend and resume callbacks for the macb driver. We may
have to do some more to gracefully shut the MAC down, but this at least
prevents the macb from waking the system when hooked up to a busy
network.
Signed-off-by: Haavard Skinnemoen <[email protected]>
Cc: Jeff Garzik <[email protected]>
Cc: Patrice Vilchez <[email protected]>
Cc: Nicolas FERRE <[email protected]>
---
drivers/net/macb.c | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 92dccd4..0a5745a 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -1277,8 +1277,45 @@ static int __exit macb_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int macb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct macb *bp = netdev_priv(netdev);
+
+ netif_device_detach(netdev);
+
+#ifndef CONFIG_ARCH_AT91
+ clk_disable(bp->hclk);
+#endif
+ clk_disable(bp->pclk);
+
+ return 0;
+}
+
+static int macb_resume(struct platform_device *pdev)
+{
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct macb *bp = netdev_priv(netdev);
+
+ clk_enable(bp->pclk);
+#ifndef CONFIG_ARCH_AT91
+ clk_enable(bp->hclk);
+#endif
+
+ netif_device_attach(netdev);
+
+ return 0;
+}
+#else
+#define macb_suspend NULL
+#define macb_resume NULL
+#endif
+
static struct platform_driver macb_driver = {
.remove = __exit_p(macb_remove),
+ .suspend = macb_suspend,
+ .resume = macb_resume,
.driver = {
.name = "macb",
.owner = THIS_MODULE,
--
1.5.5.4
From: Haavard Skinnemoen <[email protected]>
Add SRAM allocator for avr32, which is just a thin wrapper around
genalloc.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
arch/avr32/Kconfig | 1 +
arch/avr32/mach-at32ap/at32ap700x.c | 26 ++++++++++++++++++++++++++
include/asm-avr32/arch-at32ap/sram.h | 30 ++++++++++++++++++++++++++++++
3 files changed, 57 insertions(+), 0 deletions(-)
create mode 100644 include/asm-avr32/arch-at32ap/sram.h
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index 09ad799..4ff3dea 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -88,6 +88,7 @@ config PLATFORM_AT32AP
select MMU
select PERFORMANCE_COUNTERS
select HAVE_GPIO_LIB
+ select GENERIC_ALLOCATOR
#
# CPU types
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 3ee5e72..07b21b1 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -20,6 +20,7 @@
#include <asm/arch/at32ap700x.h>
#include <asm/arch/board.h>
#include <asm/arch/portmux.h>
+#include <asm/arch/sram.h>
#include <video/atmel_lcdc.h>
@@ -2120,3 +2121,28 @@ void __init setup_platform(void)
at32_init_pio(&pio3_device);
at32_init_pio(&pio4_device);
}
+
+struct gen_pool *sram_pool;
+
+static int __init sram_init(void)
+{
+ struct gen_pool *pool;
+
+ /* 1KiB granularity */
+ pool = gen_pool_create(10, -1);
+ if (!pool)
+ goto fail;
+
+ if (gen_pool_add(pool, 0x24000000, 0x8000, -1))
+ goto err_pool_add;
+
+ sram_pool = pool;
+ return 0;
+
+err_pool_add:
+ gen_pool_destroy(pool);
+fail:
+ pr_err("Failed to create SRAM pool\n");
+ return -ENOMEM;
+}
+core_initcall(sram_init);
diff --git a/include/asm-avr32/arch-at32ap/sram.h b/include/asm-avr32/arch-at32ap/sram.h
new file mode 100644
index 0000000..4838dae
--- /dev/null
+++ b/include/asm-avr32/arch-at32ap/sram.h
@@ -0,0 +1,30 @@
+/*
+ * Simple SRAM allocator
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_AVR32_ARCH_SRAM_H
+#define __ASM_AVR32_ARCH_SRAM_H
+
+#include <linux/genalloc.h>
+
+extern struct gen_pool *sram_pool;
+
+static inline unsigned long sram_alloc(size_t len)
+{
+ if (!sram_pool)
+ return 0UL;
+
+ return gen_pool_alloc(sram_pool, len);
+}
+
+static inline void sram_free(unsigned long addr, size_t len)
+{
+ return gen_pool_free(sram_pool, addr, len);
+}
+
+#endif /* __ASM_AVR32_ARCH_SRAM_H */
--
1.5.5.4