diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-07-15 20:19:14 +0100 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2008-07-15 20:19:14 +0100 |
commit | 0c17e4ceedd35c78b1c7413dbd16279a350be6bc (patch) | |
tree | 313b3b9ca04727f3704464e01d8dd97da1dd534b /arch/avr32/mach-at32ap | |
parent | 19c1d6a34abf73d0baf8e325d018c920fa78dddc (diff) | |
parent | b9d2252c1e44fa83a4e65fdc9eb93db6297c55af (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into for-rmk
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 7 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 252 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/intc.c | 80 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pdc.c (renamed from arch/avr32/mach-at32ap/at32ap.c) | 8 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.c | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.h | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pm-at32ap700x.S | 108 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pm.c | 245 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/sdramc.h | 76 |
9 files changed, 714 insertions, 66 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index e89009439e4..d5018e2eed2 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,3 +1,8 @@ -obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o +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/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 0f24b4f85c1..07b21b121ee 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> @@ -93,19 +94,12 @@ static struct clk devname##_##_name = { \ static DEFINE_SPINLOCK(pm_lock); -unsigned long at32ap7000_osc_rates[3] = { - [0] = 32768, - /* FIXME: these are ATSTK1002-specific */ - [1] = 20000000, - [2] = 12000000, -}; - static struct clk osc0; static struct clk osc1; static unsigned long osc_get_rate(struct clk *clk) { - return at32ap7000_osc_rates[clk->index]; + return at32_board_osc_rates[clk->index]; } static unsigned long pll_get_rate(struct clk *clk, unsigned long control) @@ -682,6 +676,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), @@ -841,6 +843,81 @@ void __init at32_add_system_devices(void) } /* -------------------------------------------------------------------- + * PSIF + * -------------------------------------------------------------------- */ +static struct resource atmel_psif0_resource[] __initdata = { + { + .start = 0xffe03c00, + .end = 0xffe03cff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif0_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +static struct resource atmel_psif1_resource[] __initdata = { + { + .start = 0xffe03d00, + .end = 0xffe03dff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif1_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +struct platform_device *__init at32_add_device_psif(unsigned int id) +{ + struct platform_device *pdev; + + if (!(id == 0 || id == 1)) + return NULL; + + pdev = platform_device_alloc("atmel_psif", id); + if (!pdev) + return NULL; + + switch (id) { + case 0: + if (platform_device_add_resources(pdev, atmel_psif0_resource, + ARRAY_SIZE(atmel_psif0_resource))) + goto err_add_resources; + atmel_psif0_pclk.dev = &pdev->dev; + select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + break; + case 1: + if (platform_device_add_resources(pdev, atmel_psif1_resource, + ARRAY_SIZE(atmel_psif1_resource))) + goto err_add_resources; + atmel_psif1_pclk.dev = &pdev->dev; + select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + break; + default: + return NULL; + } + + platform_device_add(pdev); + return pdev; + +err_add_resources: + platform_device_put(pdev); + return NULL; +} + +/* -------------------------------------------------------------------- * USART * -------------------------------------------------------------------- */ @@ -1113,7 +1190,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) switch (id) { case 0: pdev = &atmel_spi0_device; - select_peripheral(PA(0), PERIPH_A, 0); /* MISO */ + /* pullup MISO so a level is always defined */ + select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP); select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ at32_spi_setup_slaves(0, b, n, spi0_pins); @@ -1121,7 +1199,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) case 1: pdev = &atmel_spi1_device; - select_peripheral(PB(0), PERIPH_B, 0); /* MISO */ + /* pullup MISO so a level is always defined */ + select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP); select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */ select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ at32_spi_setup_slaves(1, b, n, spi1_pins); @@ -1264,7 +1343,8 @@ static struct clk atmel_lcdfb0_pixclk = { struct platform_device *__init at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, - unsigned long fbmem_start, unsigned long fbmem_len) + unsigned long fbmem_start, unsigned long fbmem_len, + unsigned int pin_config) { struct platform_device *pdev; struct atmel_lcdfb_info *info; @@ -1291,37 +1371,77 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, switch (id) { case 0: pdev = &atmel_lcdfb0_device; - select_peripheral(PC(19), PERIPH_A, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ - select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ - select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ - select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ - select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ - select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ - select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ - select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ - select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ - select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ - select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ - select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + + switch (pin_config) { + case 0: + select_peripheral(PC(19), PERIPH_A, 0); /* CC */ + select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ + select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ + select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ + select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ + select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ + select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ + select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ + select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ + select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ + select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ + select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ + select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ + select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ + select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ + select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ + select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ + select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ + select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ + select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ + select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ + select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ + select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + break; + case 1: + select_peripheral(PE(0), PERIPH_B, 0); /* CC */ + select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ + select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */ + select_peripheral(PE(2), PERIPH_B, 0); /* MODE */ + select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ + select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */ + select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */ + select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */ + select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */ + select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */ + select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */ + select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */ + select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */ + select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */ + select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */ + select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ + select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ + select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ + select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */ + select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */ + select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */ + select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */ + select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */ + select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */ + select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ + select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + break; + default: + goto err_invalid_id; + } clk_set_parent(&atmel_lcdfb0_pixclk, &pll0); clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0)); @@ -1360,7 +1480,7 @@ static struct resource atmel_pwm0_resource[] __initdata = { IRQ(24), }; static struct clk atmel_pwm0_mck = { - .name = "mck", + .name = "pwm_clk", .parent = &pbb_clk, .mode = pbb_clk_mode, .get_rate = pbb_clk_get_rate, @@ -1887,6 +2007,7 @@ struct clk *at32_clock_list[] = { &hmatrix_clk, &ebi_clk, &hramc_clk, + &sdramc_clk, &smc0_pclk, &smc0_mck, &pdc_hclk, @@ -1900,6 +2021,8 @@ struct clk *at32_clock_list[] = { &pio4_mck, &at32_tcb0_t0_clk, &at32_tcb1_t0_clk, + &atmel_psif0_pclk, + &atmel_psif1_pclk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, @@ -1935,16 +2058,7 @@ struct clk *at32_clock_list[] = { }; unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); -void __init at32_portmux_init(void) -{ - at32_init_pio(&pio0_device); - at32_init_pio(&pio1_device); - at32_init_pio(&pio2_device); - at32_init_pio(&pio3_device); - at32_init_pio(&pio4_device); -} - -void __init at32_clock_init(void) +void __init setup_platform(void) { u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; int i; @@ -1999,4 +2113,36 @@ void __init at32_clock_init(void) pm_writel(HSB_MASK, hsb_mask); pm_writel(PBA_MASK, pba_mask); pm_writel(PBB_MASK, pbb_mask); + + /* Initialize the port muxes */ + at32_init_pio(&pio0_device); + at32_init_pio(&pio1_device); + at32_init_pio(&pio2_device); + 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/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c index 097cf4e8405..994c4545e2b 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,20 @@ #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; +#ifdef CONFIG_PM + unsigned long suspend_ipr; + unsigned long saved_ipr[64]; +#endif }; extern struct platform_device at32_intc0_device; @@ -136,6 +142,74 @@ 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", + .suspend = intc_suspend, + .resume = intc_resume, +}; + +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); diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/pdc.c index 7c4987f3287..1040bda4fda 100644 --- a/arch/avr32/mach-at32ap/at32ap.c +++ b/arch/avr32/mach-at32ap/pdc.c @@ -11,14 +11,6 @@ #include <linux/init.h> #include <linux/platform_device.h> -#include <asm/arch/init.h> - -void __init setup_platform(void) -{ - at32_clock_init(); - at32_portmux_init(); -} - static int __init pdc_probe(struct platform_device *pdev) { struct clk *pclk, *hclk; diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 38a8fa31c0b..60da03ba711 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -318,6 +318,8 @@ static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip) const char *label; label = gpiochip_is_requested(chip, i); + if (!label && (imr & mask)) + label = "[irq]"; if (!label) continue; diff --git a/arch/avr32/mach-at32ap/pio.h b/arch/avr32/mach-at32ap/pio.h index 7795116a483..9484dfcc08f 100644 --- a/arch/avr32/mach-at32ap/pio.h +++ b/arch/avr32/mach-at32ap/pio.h @@ -57,7 +57,7 @@ /* Bitfields in IFDR */ -/* Bitfields in ISFR */ +/* Bitfields in IFSR */ /* Bitfields in SODR */ diff --git a/arch/avr32/mach-at32ap/pm-at32ap700x.S b/arch/avr32/mach-at32ap/pm-at32ap700x.S index 949e2485e27..0a53ad314ff 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 00000000000..0b764320135 --- /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 00000000000..66eeaed4907 --- /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) |