diff options
Diffstat (limited to 'arch/avr32')
-rw-r--r-- | arch/avr32/Kconfig | 9 | ||||
-rw-r--r-- | arch/avr32/boards/atngw100/setup.c | 36 | ||||
-rw-r--r-- | arch/avr32/boards/atstk1000/atstk1002.c | 11 | ||||
-rw-r--r-- | arch/avr32/boards/atstk1000/atstk1003.c | 7 | ||||
-rw-r--r-- | arch/avr32/boards/atstk1000/atstk1004.c | 9 | ||||
-rw-r--r-- | arch/avr32/kernel/entry-avr32b.S | 88 | ||||
-rw-r--r-- | arch/avr32/kernel/signal.c | 3 | ||||
-rw-r--r-- | arch/avr32/kernel/stacktrace.c | 1 | ||||
-rw-r--r-- | arch/avr32/kernel/time.c | 14 | ||||
-rw-r--r-- | arch/avr32/kernel/vmlinux.lds.S | 14 | ||||
-rw-r--r-- | arch/avr32/lib/io-readsb.S | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 7 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 282 | ||||
-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 | ||||
-rw-r--r-- | arch/avr32/mm/init.c | 22 | ||||
-rw-r--r-- | arch/avr32/mm/tlb.c | 175 |
22 files changed, 969 insertions, 232 deletions
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 09ad799..45d63c9 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 @@ -147,6 +148,9 @@ config PHYS_OFFSET source "kernel/Kconfig.preempt" +config QUICKLIST + def_bool y + config HAVE_ARCH_BOOTMEM_NODE def_bool n @@ -201,6 +205,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/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index a398be2..c7fe94d 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -9,6 +9,8 @@ */ #include <linux/clk.h> #include <linux/etherdevice.h> +#include <linux/irq.h> +#include <linux/i2c.h> #include <linux/i2c-gpio.h> #include <linux/init.h> #include <linux/linkage.h> @@ -17,6 +19,7 @@ #include <linux/leds.h> #include <linux/spi/spi.h> +#include <asm/atmel-mci.h> #include <asm/io.h> #include <asm/setup.h> @@ -25,6 +28,13 @@ #include <asm/arch/init.h> #include <asm/arch/portmux.h> +/* Oscillator frequencies. These are board-specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + /* Initialized by bootloader-specific startup code. */ struct tag *bootloader_tags __initdata; @@ -42,6 +52,11 @@ static struct spi_board_info spi0_board_info[] __initdata = { }, }; +static struct mci_platform_data __initdata mci0_data = { + .detect_pin = GPIO_PIN_PC(25), + .wp_pin = GPIO_PIN_PE(0), +}; + /* * The next two functions should go away as the boot loader is * supposed to initialize the macb address registers with a valid @@ -140,6 +155,10 @@ static struct platform_device i2c_gpio_device = { }, }; +static struct i2c_board_info __initdata i2c_info[] = { + /* NOTE: original ATtiny24 firmware is at address 0x0b */ +}; + static int __init atngw100_init(void) { unsigned i; @@ -157,6 +176,7 @@ static int __init atngw100_init(void) set_hw_addr(at32_add_device_eth(1, ð_data[1])); at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); + at32_add_device_mci(0, &mci0_data); at32_add_device_usba(0, NULL); for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) { @@ -165,12 +185,28 @@ static int __init atngw100_init(void) } platform_device_register(&ngw_gpio_leds); + /* all these i2c/smbus pins should have external pullups for + * open-drain sharing among all I2C devices. SDA and SCL do; + * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus), + * but it's not available off-board. + */ + at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP); at32_select_gpio(i2c_gpio_data.sda_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); at32_select_gpio(i2c_gpio_data.scl_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); platform_device_register(&i2c_gpio_device); + i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info)); return 0; } postcore_initcall(atngw100_init); + +static int __init atngw100_arch_init(void) +{ + /* set_irq_type() after the arch_initcall for EIC has run, and + * before the I2C subsystem could try using this IRQ. + */ + return set_irq_type(AT32_EXTINT(3), IRQ_TYPE_EDGE_FALLING); +} +arch_initcall(atngw100_arch_init); diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index 000eb42..e11659b 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -28,6 +28,12 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; struct eth_addr { u8 addr[6]; @@ -228,11 +234,14 @@ static int __init atstk1002_init(void) #ifdef CONFIG_BOARD_ATSTK100X_SPI1 at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif +#ifndef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM + at32_add_device_mci(0, NULL); +#endif #ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM set_hw_addr(at32_add_device_eth(1, ð_data[1])); #else at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size); + fbmem_start, fbmem_size, 0); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c index a0b223d..ea109f4 100644 --- a/arch/avr32/boards/atstk1000/atstk1003.c +++ b/arch/avr32/boards/atstk1000/atstk1003.c @@ -27,6 +27,13 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static struct at73c213_board_info at73c213_data = { .ssc_id = 0, diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index e765a86..c7236df 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -29,6 +29,13 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static struct at73c213_board_info at73c213_data = { .ssc_id = 0, @@ -133,7 +140,7 @@ static int __init atstk1004_init(void) at32_add_device_mci(0); #endif at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size); + fbmem_start, fbmem_size, 0); at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM at32_add_device_ssc(0, ATMEL_SSC_TX); diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index 5f31702..2b398ca 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S @@ -74,50 +74,41 @@ exception_vectors: .align 2 bral do_dtlb_modified - /* - * r0 : PGD/PT/PTE - * r1 : Offending address - * r2 : Scratch register - * r3 : Cause (5, 12 or 13) - */ #define tlbmiss_save pushm r0-r3 #define tlbmiss_restore popm r0-r3 - .section .tlbx.ex.text,"ax",@progbits + .org 0x50 .global itlb_miss itlb_miss: tlbmiss_save rjmp tlb_miss_common - .section .tlbr.ex.text,"ax",@progbits + .org 0x60 dtlb_miss_read: tlbmiss_save rjmp tlb_miss_common - .section .tlbw.ex.text,"ax",@progbits + .org 0x70 dtlb_miss_write: tlbmiss_save .global tlb_miss_common + .align 2 tlb_miss_common: mfsr r0, SYSREG_TLBEAR mfsr r1, SYSREG_PTBR - /* Is it the vmalloc space? */ - bld r0, 31 - brcs handle_vmalloc_miss - - /* First level lookup */ + /* + * First level lookup: The PGD contains virtual pointers to + * the second-level page tables, but they may be NULL if not + * present. + */ pgtbl_lookup: lsr r2, r0, PGDIR_SHIFT ld.w r3, r1[r2 << 2] bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT - bld r3, _PAGE_BIT_PRESENT - brcc page_table_not_present - - /* Translate to virtual address in P1. */ - andl r3, 0xf000 - sbr r3, 31 + cp.w r3, 0 + breq page_table_not_present /* Second level lookup */ ld.w r2, r3[r1 << 2] @@ -148,16 +139,55 @@ pgtbl_lookup: tlbmiss_restore rete -handle_vmalloc_miss: - /* Simply do the lookup in init's page table */ + /* The slow path of the TLB miss handler */ + .align 2 +page_table_not_present: + /* Do we need to synchronize with swapper_pg_dir? */ + bld r0, 31 + brcs sync_with_swapper_pg_dir + +page_not_present: + tlbmiss_restore + sub sp, 4 + stmts --sp, r0-lr + rcall save_full_context_ex + mfsr r12, SYSREG_ECR + mov r11, sp + rcall do_page_fault + rjmp ret_from_exception + + .align 2 +sync_with_swapper_pg_dir: + /* + * If swapper_pg_dir contains a non-NULL second-level page + * table pointer, copy it into the current PGD. If not, we + * must handle it as a full-blown page fault. + * + * Jumping back to pgtbl_lookup causes an unnecessary lookup, + * but it is guaranteed to be a cache hit, it won't happen + * very often, and we absolutely do not want to sacrifice any + * performance in the fast path in order to improve this. + */ mov r1, lo(swapper_pg_dir) orh r1, hi(swapper_pg_dir) + ld.w r3, r1[r2 << 2] + cp.w r3, 0 + breq page_not_present + mfsr r1, SYSREG_PTBR + st.w r1[r2 << 2], r3 rjmp pgtbl_lookup + /* + * We currently have two bytes left at this point until we + * crash into the system call handler... + * + * Don't worry, the assembler will let us know. + */ + /* --- System Call --- */ - .section .scall.text,"ax",@progbits + .org 0x100 system_call: #ifdef CONFIG_PREEMPT mask_interrupts @@ -266,18 +296,6 @@ syscall_exit_work: brcc syscall_exit_cont rjmp enter_monitor_mode - /* The slow path of the TLB miss handler */ -page_table_not_present: -page_not_present: - tlbmiss_restore - sub sp, 4 - stmts --sp, r0-lr - rcall save_full_context_ex - mfsr r12, SYSREG_ECR - mov r11, sp - rcall do_page_fault - rjmp ret_from_exception - /* This function expects to find offending PC in SYSREG_RAR_EX */ .type save_full_context_ex, @function .align 2 diff --git a/arch/avr32/kernel/signal.c b/arch/avr32/kernel/signal.c index 5616a00..c5b11f9 100644 --- a/arch/avr32/kernel/signal.c +++ b/arch/avr32/kernel/signal.c @@ -93,6 +93,9 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; + if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT) + goto badframe; + pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n", regs->pc, regs->lr, regs->sp); diff --git a/arch/avr32/kernel/stacktrace.c b/arch/avr32/kernel/stacktrace.c index 9a68190..f4bdb44 100644 --- a/arch/avr32/kernel/stacktrace.c +++ b/arch/avr32/kernel/stacktrace.c @@ -51,3 +51,4 @@ void save_stack_trace(struct stack_trace *trace) fp = frame->fp; } } +EXPORT_SYMBOL_GPL(save_stack_trace); diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c index 00a9862..abd954f 100644 --- a/arch/avr32/kernel/time.c +++ b/arch/avr32/kernel/time.c @@ -7,21 +7,13 @@ */ #include <linux/clk.h> #include <linux/clockchips.h> -#include <linux/time.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/profile.h> -#include <linux/sysdev.h> -#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/time.h> -#include <asm/div64.h> #include <asm/sysreg.h> -#include <asm/io.h> -#include <asm/sections.h> #include <asm/arch/pm.h> diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S index 481cfd4..7910d41 100644 --- a/arch/avr32/kernel/vmlinux.lds.S +++ b/arch/avr32/kernel/vmlinux.lds.S @@ -68,14 +68,6 @@ SECTIONS _evba = .; _text = .; *(.ex.text) - . = 0x50; - *(.tlbx.ex.text) - . = 0x60; - *(.tlbr.ex.text) - . = 0x70; - *(.tlbw.ex.text) - . = 0x100; - *(.scall.text) *(.irq.text) KPROBES_TEXT TEXT_TEXT @@ -93,8 +85,6 @@ SECTIONS __stop___ex_table = .; } - BUG_TABLE - RODATA . = ALIGN(THREAD_SIZE); @@ -107,6 +97,10 @@ SECTIONS */ *(.data.init_task) + /* Then, the page-aligned data */ + . = ALIGN(PAGE_SIZE); + *(.data.page_aligned) + /* Then, the cacheline aligned data */ . = ALIGN(L1_CACHE_BYTES); *(.data.cacheline_aligned) diff --git a/arch/avr32/lib/io-readsb.S b/arch/avr32/lib/io-readsb.S index 2be5da7..cb2d869 100644 --- a/arch/avr32/lib/io-readsb.S +++ b/arch/avr32/lib/io-readsb.S @@ -41,7 +41,7 @@ __raw_readsb: 2: sub r10, -4 reteq r12 -3: ld.uh r8, r12[0] +3: ld.ub r8, r12[0] sub r10, 1 st.b r11++, r8 brne 3b diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index e890094..d5018e2 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 0f24b4f8..021d512 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -14,12 +14,14 @@ #include <linux/spi/spi.h> #include <linux/usb/atmel_usba_udc.h> +#include <asm/atmel-mci.h> #include <asm/io.h> #include <asm/irq.h> #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 +95,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 +677,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 +844,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 +1191,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 +1200,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); @@ -1199,20 +1279,32 @@ static struct clk atmel_mci0_pclk = { .index = 9, }; -struct platform_device *__init at32_add_device_mci(unsigned int id) +struct platform_device *__init +at32_add_device_mci(unsigned int id, struct mci_platform_data *data) { - struct platform_device *pdev; + struct mci_platform_data _data; + struct platform_device *pdev; + struct dw_dma_slave *dws; if (id != 0) return NULL; pdev = platform_device_alloc("atmel_mci", id); if (!pdev) - return NULL; + goto fail; if (platform_device_add_resources(pdev, atmel_mci0_resource, ARRAY_SIZE(atmel_mci0_resource))) - goto err_add_resources; + goto fail; + + if (!data) { + data = &_data; + memset(data, 0, sizeof(struct mci_platform_data)); + } + + if (platform_device_add_data(pdev, data, + sizeof(struct mci_platform_data))) + goto fail; select_peripheral(PA(10), PERIPH_A, 0); /* CLK */ select_peripheral(PA(11), PERIPH_A, 0); /* CMD */ @@ -1221,12 +1313,19 @@ struct platform_device *__init at32_add_device_mci(unsigned int id) select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */ select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */ + if (data) { + if (data->detect_pin != GPIO_PIN_NONE) + at32_select_gpio(data->detect_pin, 0); + if (data->wp_pin != GPIO_PIN_NONE) + at32_select_gpio(data->wp_pin, 0); + } + atmel_mci0_pclk.dev = &pdev->dev; platform_device_add(pdev); return pdev; -err_add_resources: +fail: platform_device_put(pdev); return NULL; } @@ -1264,7 +1363,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 +1391,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 +1500,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 +2027,7 @@ struct clk *at32_clock_list[] = { &hmatrix_clk, &ebi_clk, &hramc_clk, + &sdramc_clk, &smc0_pclk, &smc0_mck, &pdc_hclk, @@ -1900,6 +2041,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 +2078,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 +2133,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 097cf4e..994c4545 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 7c4987f..1040bda 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 38a8fa3..60da03b 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 7795116..9484dfc 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 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/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 0e64ddc..3f90a87 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -11,6 +11,7 @@ #include <linux/swap.h> #include <linux/init.h> #include <linux/mmzone.h> +#include <linux/module.h> #include <linux/bootmem.h> #include <linux/pagemap.h> #include <linux/nodemask.h> @@ -23,11 +24,14 @@ #include <asm/setup.h> #include <asm/sections.h> +#define __page_aligned __attribute__((section(".data.page_aligned"))) + DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -pgd_t swapper_pg_dir[PTRS_PER_PGD]; +pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned; struct page *empty_zero_page; +EXPORT_SYMBOL(empty_zero_page); /* * Cache of MMU context last used. @@ -106,19 +110,9 @@ void __init paging_init(void) zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0), PAGE_SIZE); - { - pgd_t *pg_dir; - int i; - - pg_dir = swapper_pg_dir; - sysreg_write(PTBR, (unsigned long)pg_dir); - - for (i = 0; i < PTRS_PER_PGD; i++) - pgd_val(pg_dir[i]) = 0; - - enable_mmu(); - printk ("CPU: Paging enabled\n"); - } + sysreg_write(PTBR, (unsigned long)swapper_pg_dir); + enable_mmu(); + printk ("CPU: Paging enabled\n"); for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); diff --git a/arch/avr32/mm/tlb.c b/arch/avr32/mm/tlb.c index cd12edb..06677be 100644 --- a/arch/avr32/mm/tlb.c +++ b/arch/avr32/mm/tlb.c @@ -11,21 +11,21 @@ #include <asm/mmu_context.h> -#define _TLBEHI_I 0x100 +/* TODO: Get the correct number from the CONFIG1 system register */ +#define NR_TLB_ENTRIES 32 -void show_dtlb_entry(unsigned int index) +static void show_dtlb_entry(unsigned int index) { - unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save; + u32 tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save; unsigned long flags; local_irq_save(flags); mmucr_save = sysreg_read(MMUCR); tlbehi_save = sysreg_read(TLBEHI); - mmucr = mmucr_save & 0x13; - mmucr |= index << 14; + mmucr = SYSREG_BFINS(DRP, index, mmucr_save); sysreg_write(MMUCR, mmucr); - asm volatile("tlbr" : : : "memory"); + __builtin_tlbr(); cpu_sync_pipeline(); tlbehi = sysreg_read(TLBEHI); @@ -33,15 +33,17 @@ void show_dtlb_entry(unsigned int index) printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n", index, - (tlbehi & 0x200)?'1':'0', - (tlbelo & 0x100)?'1':'0', - (tlbehi & 0xff), - (tlbehi >> 12), (tlbelo >> 12), - (tlbelo >> 4) & 7, (tlbelo >> 2) & 3, - (tlbelo & 0x200)?'1':'0', - (tlbelo & 0x080)?'1':'0', - (tlbelo & 0x001)?'1':'0', - (tlbelo & 0x002)?'1':'0'); + SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0', + SYSREG_BFEXT(G, tlbelo) ? '1' : '0', + SYSREG_BFEXT(ASID, tlbehi), + SYSREG_BFEXT(VPN, tlbehi) >> 2, + SYSREG_BFEXT(PFN, tlbelo) >> 2, + SYSREG_BFEXT(AP, tlbelo), + SYSREG_BFEXT(SZ, tlbelo), + SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ', + SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ', + SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ', + SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' '); sysreg_write(MMUCR, mmucr_save); sysreg_write(TLBEHI, tlbehi_save); @@ -54,29 +56,33 @@ void dump_dtlb(void) unsigned int i; printk("ID V G ASID VPN PFN AP SZ C B W D\n"); - for (i = 0; i < 32; i++) + for (i = 0; i < NR_TLB_ENTRIES; i++) show_dtlb_entry(i); } -static unsigned long last_mmucr; - -static inline void set_replacement_pointer(unsigned shift) +static void update_dtlb(unsigned long address, pte_t pte) { - unsigned long mmucr, mmucr_save; + u32 tlbehi; + u32 mmucr; - mmucr = mmucr_save = sysreg_read(MMUCR); + /* + * We're not changing the ASID here, so no need to flush the + * pipeline. + */ + tlbehi = sysreg_read(TLBEHI); + tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi)); + tlbehi |= address & MMU_VPN_MASK; + tlbehi |= SYSREG_BIT(TLBEHI_V); + sysreg_write(TLBEHI, tlbehi); /* Does this mapping already exist? */ - __asm__ __volatile__( - " tlbs\n" - " mfsr %0, %1" - : "=r"(mmucr) - : "i"(SYSREG_MMUCR)); + __builtin_tlbs(); + mmucr = sysreg_read(MMUCR); if (mmucr & SYSREG_BIT(MMUCR_N)) { /* Not found -- pick a not-recently-accessed entry */ - unsigned long rp; - unsigned long tlbar = sysreg_read(TLBARLO); + unsigned int rp; + u32 tlbar = sysreg_read(TLBARLO); rp = 32 - fls(tlbar); if (rp == 32) { @@ -84,30 +90,14 @@ static inline void set_replacement_pointer(unsigned shift) sysreg_write(TLBARLO, -1L); } - mmucr &= 0x13; - mmucr |= (rp << shift); - + mmucr = SYSREG_BFINS(DRP, rp, mmucr); sysreg_write(MMUCR, mmucr); } - last_mmucr = mmucr; -} - -static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid) -{ - unsigned long vpn; - - vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid; - sysreg_write(TLBEHI, vpn); - cpu_sync_pipeline(); - - set_replacement_pointer(14); - sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK); /* Let's go */ - asm volatile("nop\n\ttlbw" : : : "memory"); - cpu_sync_pipeline(); + __builtin_tlbw(); } void update_mmu_cache(struct vm_area_struct *vma, @@ -120,39 +110,40 @@ void update_mmu_cache(struct vm_area_struct *vma, return; local_irq_save(flags); - update_dtlb(address, pte, get_asid()); + update_dtlb(address, pte); local_irq_restore(flags); } -void __flush_tlb_page(unsigned long asid, unsigned long page) +static void __flush_tlb_page(unsigned long asid, unsigned long page) { - unsigned long mmucr, tlbehi; + u32 mmucr, tlbehi; - page |= asid; - sysreg_write(TLBEHI, page); - cpu_sync_pipeline(); - asm volatile("tlbs"); + /* + * Caller is responsible for masking out non-PFN bits in page + * and changing the current ASID if necessary. This means that + * we don't need to flush the pipeline after writing TLBEHI. + */ + tlbehi = page | asid; + sysreg_write(TLBEHI, tlbehi); + + __builtin_tlbs(); mmucr = sysreg_read(MMUCR); if (!(mmucr & SYSREG_BIT(MMUCR_N))) { - unsigned long tlbarlo; - unsigned long entry; + unsigned int entry; + u32 tlbarlo; /* Clear the "valid" bit */ - tlbehi = sysreg_read(TLBEHI); - tlbehi &= ~_TLBEHI_VALID; sysreg_write(TLBEHI, tlbehi); - cpu_sync_pipeline(); /* mark the entry as "not accessed" */ - entry = (mmucr >> 14) & 0x3f; + entry = SYSREG_BFEXT(DRP, mmucr); tlbarlo = sysreg_read(TLBARLO); - tlbarlo |= (0x80000000 >> entry); + tlbarlo |= (0x80000000UL >> entry); sysreg_write(TLBARLO, tlbarlo); /* update the entry with valid bit clear */ - asm volatile("tlbw"); - cpu_sync_pipeline(); + __builtin_tlbw(); } } @@ -190,17 +181,22 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ mm->context = NO_CONTEXT; if (mm == current->mm) activate_context(mm); } else { - unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK; - unsigned long saved_asid = MMU_NO_ASID; + unsigned long asid; + unsigned long saved_asid; + + asid = mm->context & MMU_CONTEXT_ASID_MASK; + saved_asid = MMU_NO_ASID; start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; + if (mm != current->mm) { saved_asid = get_asid(); set_asid(asid); @@ -218,33 +214,34 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, } /* - * TODO: If this is only called for addresses > TASK_SIZE, we can probably - * skip the ASID stuff and just use the Global bit... + * This function depends on the pages to be flushed having the G + * (global) bit set in their pte. This is true for all + * PAGE_KERNEL(_RO) pages. */ void flush_tlb_kernel_range(unsigned long start, unsigned long end) { unsigned long flags; int size; - local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ flush_tlb_all(); } else { - unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK; - unsigned long saved_asid = get_asid(); + unsigned long asid; + + local_irq_save(flags); + asid = get_asid(); start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; - set_asid(asid); + while (start < end) { __flush_tlb_page(asid, start); start += PAGE_SIZE; } - set_asid(saved_asid); + local_irq_restore(flags); } - local_irq_restore(flags); } void flush_tlb_mm(struct mm_struct *mm) @@ -280,7 +277,7 @@ static void *tlb_start(struct seq_file *tlb, loff_t *pos) { static unsigned long tlb_index; - if (*pos >= 32) + if (*pos >= NR_TLB_ENTRIES) return NULL; tlb_index = 0; @@ -291,7 +288,7 @@ static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos) { unsigned long *index = v; - if (*index >= 31) + if (*index >= NR_TLB_ENTRIES - 1) return NULL; ++*pos; @@ -313,16 +310,16 @@ static int tlb_show(struct seq_file *tlb, void *v) if (*index == 0) seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n"); - BUG_ON(*index >= 32); + BUG_ON(*index >= NR_TLB_ENTRIES); local_irq_save(flags); mmucr_save = sysreg_read(MMUCR); tlbehi_save = sysreg_read(TLBEHI); - mmucr = mmucr_save & 0x13; - mmucr |= *index << 14; + mmucr = SYSREG_BFINS(DRP, *index, mmucr_save); sysreg_write(MMUCR, mmucr); - asm volatile("tlbr" : : : "memory"); + /* TLBR might change the ASID */ + __builtin_tlbr(); cpu_sync_pipeline(); tlbehi = sysreg_read(TLBEHI); @@ -334,16 +331,18 @@ static int tlb_show(struct seq_file *tlb, void *v) local_irq_restore(flags); seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n", - *index, - (tlbehi & 0x200)?'1':'0', - (tlbelo & 0x100)?'1':'0', - (tlbehi & 0xff), - (tlbehi >> 12), (tlbelo >> 12), - (tlbelo >> 4) & 7, (tlbelo >> 2) & 3, - (tlbelo & 0x200)?'1':'0', - (tlbelo & 0x080)?'1':'0', - (tlbelo & 0x001)?'1':'0', - (tlbelo & 0x002)?'1':'0'); + *index, + SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0', + SYSREG_BFEXT(G, tlbelo) ? '1' : '0', + SYSREG_BFEXT(ASID, tlbehi), + SYSREG_BFEXT(VPN, tlbehi) >> 2, + SYSREG_BFEXT(PFN, tlbelo) >> 2, + SYSREG_BFEXT(AP, tlbelo), + SYSREG_BFEXT(SZ, tlbelo), + SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '0', + SYSREG_BFEXT(B, tlbelo) ? '1' : '0', + SYSREG_BFEXT(W, tlbelo) ? '1' : '0', + SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '0'); return 0; } |