Commit 6c118e43 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen/avr32-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen/avr32-2.6: (31 commits)
  avr32: Fix typo of IFSR in a comment in the PIO header file
  avr32: Power Management support ("standby" and "mem" modes)
  avr32: Add system device for the internal interrupt controller (intc)
  avr32: Add simple SRAM allocator
  avr32: Enable SDRAMC clock at startup
  rtc-at32ap700x: Enable wakeup
  macb: Basic suspend/resume support
  atmel_serial: Drain console TX shifter before suspending
  atmel_serial: Fix build on avr32 with CONFIG_PM enabled
  avr32: Use a quicklist for PTE allocation as well
  avr32: Use a quicklist for PGD allocation
  avr32: Cover the kernel page tables in the user PGDs
  avr32: Store virtual addresses in the PGD
  avr32: Remove useless zeroing of swapper_pg_dir at startup
  avr32: Clean up and optimize the TLB operations
  avr32: Rename at32ap.c -> pdc.c
  avr32: Move setup_platform() into chip-specific file
  avr32: Kill special exception handler sections
  avr32: Kill unneeded #include <asm/pgalloc.h> from asm/mmu_context.h
  avr32: Clean up time.c #includes
  ...
parents 847106ff 72f6befe
...@@ -88,6 +88,7 @@ config PLATFORM_AT32AP ...@@ -88,6 +88,7 @@ config PLATFORM_AT32AP
select MMU select MMU
select PERFORMANCE_COUNTERS select PERFORMANCE_COUNTERS
select HAVE_GPIO_LIB select HAVE_GPIO_LIB
select GENERIC_ALLOCATOR
# #
# CPU types # CPU types
...@@ -147,6 +148,9 @@ config PHYS_OFFSET ...@@ -147,6 +148,9 @@ config PHYS_OFFSET
source "kernel/Kconfig.preempt" source "kernel/Kconfig.preempt"
config QUICKLIST
def_bool y
config HAVE_ARCH_BOOTMEM_NODE config HAVE_ARCH_BOOTMEM_NODE
def_bool n def_bool n
...@@ -201,6 +205,11 @@ endmenu ...@@ -201,6 +205,11 @@ endmenu
menu "Power management options" menu "Power management options"
source "kernel/power/Kconfig"
config ARCH_SUSPEND_POSSIBLE
def_bool y
menu "CPU Frequency scaling" menu "CPU Frequency scaling"
source "drivers/cpufreq/Kconfig" source "drivers/cpufreq/Kconfig"
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h> #include <linux/i2c-gpio.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/linkage.h> #include <linux/linkage.h>
...@@ -25,6 +27,13 @@ ...@@ -25,6 +27,13 @@
#include <asm/arch/init.h> #include <asm/arch/init.h>
#include <asm/arch/portmux.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. */ /* Initialized by bootloader-specific startup code. */
struct tag *bootloader_tags __initdata; struct tag *bootloader_tags __initdata;
...@@ -140,6 +149,10 @@ static struct platform_device i2c_gpio_device = { ...@@ -140,6 +149,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) static int __init atngw100_init(void)
{ {
unsigned i; unsigned i;
...@@ -165,12 +178,28 @@ static int __init atngw100_init(void) ...@@ -165,12 +178,28 @@ static int __init atngw100_init(void)
} }
platform_device_register(&ngw_gpio_leds); 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_select_gpio(i2c_gpio_data.sda_pin,
AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(i2c_gpio_data.scl_pin, at32_select_gpio(i2c_gpio_data.scl_pin,
AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
platform_device_register(&i2c_gpio_device); platform_device_register(&i2c_gpio_device);
i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
return 0; return 0;
} }
postcore_initcall(atngw100_init); 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);
...@@ -28,6 +28,12 @@ ...@@ -28,6 +28,12 @@
#include "atstk1000.h" #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 { struct eth_addr {
u8 addr[6]; u8 addr[6];
...@@ -232,7 +238,7 @@ static int __init atstk1002_init(void) ...@@ -232,7 +238,7 @@ static int __init atstk1002_init(void)
set_hw_addr(at32_add_device_eth(1, &eth_data[1])); set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
#else #else
at32_add_device_lcdc(0, &atstk1000_lcdc_data, at32_add_device_lcdc(0, &atstk1000_lcdc_data,
fbmem_start, fbmem_size); fbmem_start, fbmem_size, 0);
#endif #endif
at32_add_device_usba(0, NULL); at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
......
...@@ -27,6 +27,13 @@ ...@@ -27,6 +27,13 @@
#include "atstk1000.h" #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 #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static struct at73c213_board_info at73c213_data = { static struct at73c213_board_info at73c213_data = {
.ssc_id = 0, .ssc_id = 0,
......
...@@ -29,6 +29,13 @@ ...@@ -29,6 +29,13 @@
#include "atstk1000.h" #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 #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static struct at73c213_board_info at73c213_data = { static struct at73c213_board_info at73c213_data = {
.ssc_id = 0, .ssc_id = 0,
...@@ -133,7 +140,7 @@ static int __init atstk1004_init(void) ...@@ -133,7 +140,7 @@ static int __init atstk1004_init(void)
at32_add_device_mci(0); at32_add_device_mci(0);
#endif #endif
at32_add_device_lcdc(0, &atstk1000_lcdc_data, at32_add_device_lcdc(0, &atstk1000_lcdc_data,
fbmem_start, fbmem_size); fbmem_start, fbmem_size, 0);
at32_add_device_usba(0, NULL); at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
at32_add_device_ssc(0, ATMEL_SSC_TX); at32_add_device_ssc(0, ATMEL_SSC_TX);
......
...@@ -74,50 +74,41 @@ exception_vectors: ...@@ -74,50 +74,41 @@ exception_vectors:
.align 2 .align 2
bral do_dtlb_modified 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_save pushm r0-r3
#define tlbmiss_restore popm r0-r3 #define tlbmiss_restore popm r0-r3
.section .tlbx.ex.text,"ax",@progbits .org 0x50
.global itlb_miss .global itlb_miss
itlb_miss: itlb_miss:
tlbmiss_save tlbmiss_save
rjmp tlb_miss_common rjmp tlb_miss_common
.section .tlbr.ex.text,"ax",@progbits .org 0x60
dtlb_miss_read: dtlb_miss_read:
tlbmiss_save tlbmiss_save
rjmp tlb_miss_common rjmp tlb_miss_common
.section .tlbw.ex.text,"ax",@progbits .org 0x70
dtlb_miss_write: dtlb_miss_write:
tlbmiss_save tlbmiss_save
.global tlb_miss_common .global tlb_miss_common
.align 2
tlb_miss_common: tlb_miss_common:
mfsr r0, SYSREG_TLBEAR mfsr r0, SYSREG_TLBEAR
mfsr r1, SYSREG_PTBR mfsr r1, SYSREG_PTBR
/* Is it the vmalloc space? */ /*
bld r0, 31 * First level lookup: The PGD contains virtual pointers to
brcs handle_vmalloc_miss * the second-level page tables, but they may be NULL if not
* present.
/* First level lookup */ */
pgtbl_lookup: pgtbl_lookup:
lsr r2, r0, PGDIR_SHIFT lsr r2, r0, PGDIR_SHIFT
ld.w r3, r1[r2 << 2] ld.w r3, r1[r2 << 2]
bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT
bld r3, _PAGE_BIT_PRESENT cp.w r3, 0
brcc page_table_not_present breq page_table_not_present
/* Translate to virtual address in P1. */
andl r3, 0xf000
sbr r3, 31
/* Second level lookup */ /* Second level lookup */
ld.w r2, r3[r1 << 2] ld.w r2, r3[r1 << 2]
...@@ -148,16 +139,55 @@ pgtbl_lookup: ...@@ -148,16 +139,55 @@ pgtbl_lookup:
tlbmiss_restore tlbmiss_restore
rete rete
handle_vmalloc_miss: /* The slow path of the TLB miss handler */
/* Simply do the lookup in init's page table */ .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) mov r1, lo(swapper_pg_dir)
orh r1, hi(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 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 --- */ /* --- System Call --- */
.section .scall.text,"ax",@progbits .org 0x100
system_call: system_call:
#ifdef CONFIG_PREEMPT #ifdef CONFIG_PREEMPT
mask_interrupts mask_interrupts
...@@ -266,18 +296,6 @@ syscall_exit_work: ...@@ -266,18 +296,6 @@ syscall_exit_work:
brcc syscall_exit_cont brcc syscall_exit_cont
rjmp enter_monitor_mode 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 */ /* This function expects to find offending PC in SYSREG_RAR_EX */
.type save_full_context_ex, @function .type save_full_context_ex, @function
.align 2 .align 2
......
...@@ -93,6 +93,9 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) ...@@ -93,6 +93,9 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe; 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", pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n",
regs->pc, regs->lr, regs->sp); regs->pc, regs->lr, regs->sp);
......
...@@ -7,21 +7,13 @@ ...@@ -7,21 +7,13 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/time.h> #include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/kernel_stat.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/time.h>
#include <linux/init.h>
#include <linux/profile.h>
#include <linux/sysdev.h>
#include <linux/err.h>
#include <asm/div64.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
#include <asm/io.h>
#include <asm/sections.h>
#include <asm/arch/pm.h> #include <asm/arch/pm.h>
......
...@@ -68,14 +68,6 @@ SECTIONS ...@@ -68,14 +68,6 @@ SECTIONS
_evba = .; _evba = .;
_text = .; _text = .;
*(.ex.text) *(.ex.text)
. = 0x50;
*(.tlbx.ex.text)
. = 0x60;
*(.tlbr.ex.text)
. = 0x70;
*(.tlbw.ex.text)
. = 0x100;
*(.scall.text)
*(.irq.text) *(.irq.text)
KPROBES_TEXT KPROBES_TEXT
TEXT_TEXT TEXT_TEXT
...@@ -107,6 +99,10 @@ SECTIONS ...@@ -107,6 +99,10 @@ SECTIONS
*/ */
*(.data.init_task) *(.data.init_task)
/* Then, the page-aligned data */
. = ALIGN(PAGE_SIZE);
*(.data.page_aligned)
/* Then, the cacheline aligned data */ /* Then, the cacheline aligned data */
. = ALIGN(L1_CACHE_BYTES); . = ALIGN(L1_CACHE_BYTES);
*(.data.cacheline_aligned) *(.data.cacheline_aligned)
......
...@@ -41,7 +41,7 @@ __raw_readsb: ...@@ -41,7 +41,7 @@ __raw_readsb:
2: sub r10, -4 2: sub r10, -4
reteq r12 reteq r12
3: ld.uh r8, r12[0] 3: ld.ub r8, r12[0]
sub r10, 1 sub r10, 1
st.b r11++, r8 st.b r11++, r8
brne 3b brne 3b
......
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_AT32AP700X) += at32ap700x.o pm-at32ap700x.o
obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o
obj-$(CONFIG_PM) += pm.o
ifeq ($(CONFIG_PM_DEBUG),y)
CFLAGS_pm.o += -DDEBUG
endif
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <asm/arch/at32ap700x.h> #include <asm/arch/at32ap700x.h>
#include <asm/arch/board.h> #include <asm/arch/board.h>
#include <asm/arch/portmux.h> #include <asm/arch/portmux.h>
#include <asm/arch/sram.h>
#include <video/atmel_lcdc.h> #include <video/atmel_lcdc.h>
...@@ -93,19 +94,12 @@ static struct clk devname##_##_name = { \ ...@@ -93,19 +94,12 @@ static struct clk devname##_##_name = { \
static DEFINE_SPINLOCK(pm_lock); 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 osc0;
static struct clk osc1; static struct clk osc1;
static unsigned long osc_get_rate(struct clk *clk) 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) static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
...@@ -682,6 +676,14 @@ static struct clk hramc_clk = { ...@@ -682,6 +676,14 @@ static struct clk hramc_clk = {
.users = 1, .users = 1,
.index = 3, .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[] = { static struct resource smc0_resource[] = {
PBMEM(0xfff03400), PBMEM(0xfff03400),
...@@ -840,6 +842,81 @@ void __init at32_add_system_devices(void) ...@@ -840,6 +842,81 @@ void __init at32_add_system_devices(void)
platform_device_register(&pio4_device); platform_device_register(&pio4_device);
} }
/* --------------------------------------------------------------------
* 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 * USART
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -1113,7 +1190,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) ...@@ -1113,7 +1190,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
switch (id) { switch (id) {
case 0: case 0:
pdev = &atmel_spi0_device; 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(1), PERIPH_A, 0); /* MOSI */
select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ select_peripheral(PA(2), PERIPH_A, 0); /* SCK */
at32_spi_setup_slaves(0, b, n, spi0_pins); 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) ...@@ -1121,7 +1199,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
case 1: case 1:
pdev = &atmel_spi1_device; 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(1), PERIPH_B, 0); /* MOSI */
select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ select_peripheral(PB(5), PERIPH_B, 0); /* SCK */
at32_spi_setup_slaves(1, b, n, spi1_pins); at32_spi_setup_slaves(1, b, n, spi1_pins);
...@@ -1264,7 +1343,8 @@ static struct clk atmel_lcdfb0_pixclk = { ...@@ -1264,7 +1343,8 @@ static struct clk atmel_lcdfb0_pixclk = {
struct platform_device *__init struct platform_device *__init
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, 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 platform_device *pdev;
struct atmel_lcdfb_info *info; struct atmel_lcdfb_info *info;
...@@ -1291,37 +1371,77 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, ...@@ -1291,37 +1371,77 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
switch (id) { switch (id) {
case 0: case 0:
pdev = &atmel_lcdfb0_device; pdev = &atmel_lcdfb0_device;
select_peripheral(PC(19), PERIPH_A, 0); /* CC */
select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ switch (pin_config) {
select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ case 0:
select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ select_peripheral(PC(19), PERIPH_A, 0); /* CC */
select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */
select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ select_peripheral(PC(24), PERIPH_A, 0); /* MODE */
select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */
select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */
select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */
select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */
select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */
select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */
select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */
select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */
select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */
select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */
select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */
select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */
select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */
select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */
select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ 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_parent(&atmel_lcdfb0_pixclk, &pll0);
clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0)); clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
...@@ -1360,7 +1480,7 @@ static struct resource atmel_pwm0_resource[] __initdata = { ...@@ -1360,7 +1480,7 @@ static struct resource atmel_pwm0_resource[] __initdata = {
IRQ(24), IRQ(24),
}; };
static struct clk atmel_pwm0_mck = { static struct clk atmel_pwm0_mck = {
.name = "mck", .name = "pwm_clk",
.parent = &pbb_clk, .parent = &pbb_clk,
.mode = pbb_clk_mode, .mode = pbb_clk_mode,
.get_rate = pbb_clk_get_rate, .get_rate = pbb_clk_get_rate,
...@@ -1887,6 +2007,7 @@ struct clk *at32_clock_list[] = { ...@@ -1887,6 +2007,7 @@ struct clk *at32_clock_list[] = {
&hmatrix_clk, &hmatrix_clk,
&ebi_clk, &ebi_clk,
&hramc_clk, &hramc_clk,
&sdramc_clk,
&smc0_pclk, &smc0_pclk,
&smc0_mck, &smc0_mck,
&pdc_hclk, &pdc_hclk,
...@@ -1900,6 +2021,8 @@ struct clk *at32_clock_list[] = { ...@@ -1900,6 +2021,8 @@ struct clk *at32_clock_list[] = {
&pio4_mck, &pio4_mck,
&at32_tcb0_t0_clk, &at32_tcb0_t0_clk,
&at32_tcb1_t0_clk, &at32_tcb1_t0_clk,
&atmel_psif0_pclk,
&atmel_psif1_pclk,
&atmel_usart0_usart, &atmel_usart0_usart,
&atmel_usart1_usart, &atmel_usart1_usart,
&atmel_usart2_usart, &atmel_usart2_usart,
...@@ -1935,16 +2058,7 @@ struct clk *at32_clock_list[] = { ...@@ -1935,16 +2058,7 @@ struct clk *at32_clock_list[] = {
}; };
unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
void __init at32_portmux_init(void) void __init setup_platform(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)
{ {
u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
int i; int i;
...@@ -1999,4 +2113,36 @@ void __init at32_clock_init(void) ...@@ -1999,4 +2113,36 @@ void __init at32_clock_init(void)
pm_writel(HSB_MASK, hsb_mask); pm_writel(HSB_MASK, hsb_mask);
pm_writel(PBA_MASK, pba_mask); pm_writel(PBA_MASK, pba_mask);
pm_writel(PBB_MASK, pbb_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);
/* /*
* Copyright (C) 2006 Atmel Corporation * Copyright (C) 2006, 2008 Atmel Corporation
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -12,14 +12,20 @@ ...@@ -12,14 +12,20 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <asm/io.h> #include <asm/io.h>
#include "intc.h" #include "intc.h"
struct intc { struct intc {
void __iomem *regs; void __iomem *regs;
struct irq_chip chip; 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; extern struct platform_device at32_intc0_device;
...@@ -136,6 +142,74 @@ fail: ...@@ -136,6 +142,74 @@ fail:
panic("Interrupt controller initialization failed!\n"); 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) unsigned long intc_get_pending(unsigned int group)
{ {
return intc_readl(&intc0, INTREQ0 + 4 * group); return intc_readl(&intc0, INTREQ0 + 4 * group);
......
...@@ -11,14 +11,6 @@ ...@@ -11,14 +11,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.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) static int __init pdc_probe(struct platform_device *pdev)
{ {
struct clk *pclk, *hclk; struct clk *pclk, *hclk;
......
...@@ -318,6 +318,8 @@ static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip) ...@@ -318,6 +318,8 @@ static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip)
const char *label; const char *label;
label = gpiochip_is_requested(chip, i); label = gpiochip_is_requested(chip, i);
if (!label && (imr & mask))
label = "[irq]";
if (!label) if (!label)
continue; continue;
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
/* Bitfields in IFDR */ /* Bitfields in IFDR */
/* Bitfields in ISFR */ /* Bitfields in IFSR */
/* Bitfields in SODR */ /* Bitfields in SODR */
......
...@@ -12,6 +12,12 @@ ...@@ -12,6 +12,12 @@
#include <asm/thread_info.h> #include <asm/thread_info.h>
#include <asm/arch/pm.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 .section .bss, "wa", @nobits
.global disable_idle_sleep .global disable_idle_sleep
.type disable_idle_sleep, @object .type disable_idle_sleep, @object
...@@ -64,3 +70,105 @@ cpu_idle_skip_sleep: ...@@ -64,3 +70,105 @@ cpu_idle_skip_sleep:
unmask_interrupts unmask_interrupts
retal r12 retal r12
.size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep .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 */
/*
* 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);
/*
* 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)
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mmzone.h> #include <linux/mmzone.h>
#include <linux/module.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/nodemask.h> #include <linux/nodemask.h>
...@@ -23,11 +24,14 @@ ...@@ -23,11 +24,14 @@
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/sections.h> #include <asm/sections.h>
#define __page_aligned __attribute__((section(".data.page_aligned")))
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); 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; struct page *empty_zero_page;
EXPORT_SYMBOL(empty_zero_page);
/* /*
* Cache of MMU context last used. * Cache of MMU context last used.
...@@ -106,19 +110,9 @@ void __init paging_init(void) ...@@ -106,19 +110,9 @@ void __init paging_init(void)
zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0), zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0),
PAGE_SIZE); PAGE_SIZE);
{ sysreg_write(PTBR, (unsigned long)swapper_pg_dir);
pgd_t *pg_dir; enable_mmu();
int i; printk ("CPU: Paging enabled\n");
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");
}
for_each_online_node(nid) { for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid); pg_data_t *pgdat = NODE_DATA(nid);
......
...@@ -11,21 +11,21 @@ ...@@ -11,21 +11,21 @@
#include <asm/mmu_context.h> #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; unsigned long flags;
local_irq_save(flags); local_irq_save(flags);
mmucr_save = sysreg_read(MMUCR); mmucr_save = sysreg_read(MMUCR);
tlbehi_save = sysreg_read(TLBEHI); tlbehi_save = sysreg_read(TLBEHI);
mmucr = mmucr_save & 0x13; mmucr = SYSREG_BFINS(DRP, index, mmucr_save);
mmucr |= index << 14;
sysreg_write(MMUCR, mmucr); sysreg_write(MMUCR, mmucr);
asm volatile("tlbr" : : : "memory"); __builtin_tlbr();
cpu_sync_pipeline(); cpu_sync_pipeline();
tlbehi = sysreg_read(TLBEHI); tlbehi = sysreg_read(TLBEHI);
...@@ -33,15 +33,17 @@ void show_dtlb_entry(unsigned int index) ...@@ -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", printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
index, index,
(tlbehi & 0x200)?'1':'0', SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
(tlbelo & 0x100)?'1':'0', SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
(tlbehi & 0xff), SYSREG_BFEXT(ASID, tlbehi),
(tlbehi >> 12), (tlbelo >> 12), SYSREG_BFEXT(VPN, tlbehi) >> 2,
(tlbelo >> 4) & 7, (tlbelo >> 2) & 3, SYSREG_BFEXT(PFN, tlbelo) >> 2,
(tlbelo & 0x200)?'1':'0', SYSREG_BFEXT(AP, tlbelo),
(tlbelo & 0x080)?'1':'0', SYSREG_BFEXT(SZ, tlbelo),
(tlbelo & 0x001)?'1':'0', SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ',
(tlbelo & 0x002)?'1':'0'); SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ',
SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ',
SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' ');
sysreg_write(MMUCR, mmucr_save); sysreg_write(MMUCR, mmucr_save);
sysreg_write(TLBEHI, tlbehi_save); sysreg_write(TLBEHI, tlbehi_save);
...@@ -54,29 +56,33 @@ void dump_dtlb(void) ...@@ -54,29 +56,33 @@ void dump_dtlb(void)
unsigned int i; unsigned int i;
printk("ID V G ASID VPN PFN AP SZ C B W D\n"); 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); show_dtlb_entry(i);
} }
static unsigned long last_mmucr; static void update_dtlb(unsigned long address, pte_t pte)
static inline void set_replacement_pointer(unsigned shift)
{ {
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? */ /* Does this mapping already exist? */
__asm__ __volatile__( __builtin_tlbs();
" tlbs\n" mmucr = sysreg_read(MMUCR);
" mfsr %0, %1"
: "=r"(mmucr)
: "i"(SYSREG_MMUCR));
if (mmucr & SYSREG_BIT(MMUCR_N)) { if (mmucr & SYSREG_BIT(MMUCR_N)) {
/* Not found -- pick a not-recently-accessed entry */ /* Not found -- pick a not-recently-accessed entry */
unsigned long rp; unsigned int rp;
unsigned long tlbar = sysreg_read(TLBARLO); u32 tlbar = sysreg_read(TLBARLO);
rp = 32 - fls(tlbar); rp = 32 - fls(tlbar);
if (rp == 32) { if (rp == 32) {
...@@ -84,30 +90,14 @@ static inline void set_replacement_pointer(unsigned shift) ...@@ -84,30 +90,14 @@ static inline void set_replacement_pointer(unsigned shift)
sysreg_write(TLBARLO, -1L); sysreg_write(TLBARLO, -1L);
} }
mmucr &= 0x13; mmucr = SYSREG_BFINS(DRP, rp, mmucr);
mmucr |= (rp << shift);
sysreg_write(MMUCR, 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); sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
/* Let's go */ /* Let's go */
asm volatile("nop\n\ttlbw" : : : "memory"); __builtin_tlbw();
cpu_sync_pipeline();
} }
void update_mmu_cache(struct vm_area_struct *vma, void update_mmu_cache(struct vm_area_struct *vma,
...@@ -120,39 +110,40 @@ void update_mmu_cache(struct vm_area_struct *vma, ...@@ -120,39 +110,40 @@ void update_mmu_cache(struct vm_area_struct *vma,
return; return;
local_irq_save(flags); local_irq_save(flags);
update_dtlb(address, pte, get_asid()); update_dtlb(address, pte);
local_irq_restore(flags); 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); * Caller is responsible for masking out non-PFN bits in page
cpu_sync_pipeline(); * and changing the current ASID if necessary. This means that
asm volatile("tlbs"); * we don't need to flush the pipeline after writing TLBEHI.
*/
tlbehi = page | asid;
sysreg_write(TLBEHI, tlbehi);
__builtin_tlbs();
mmucr = sysreg_read(MMUCR); mmucr = sysreg_read(MMUCR);
if (!(mmucr & SYSREG_BIT(MMUCR_N))) { if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
unsigned long tlbarlo; unsigned int entry;
unsigned long entry; u32 tlbarlo;
/* Clear the "valid" bit */ /* Clear the "valid" bit */
tlbehi = sysreg_read(TLBEHI);
tlbehi &= ~_TLBEHI_VALID;
sysreg_write(TLBEHI, tlbehi); sysreg_write(TLBEHI, tlbehi);
cpu_sync_pipeline();
/* mark the entry as "not accessed" */ /* mark the entry as "not accessed" */
entry = (mmucr >> 14) & 0x3f; entry = SYSREG_BFEXT(DRP, mmucr);
tlbarlo = sysreg_read(TLBARLO); tlbarlo = sysreg_read(TLBARLO);
tlbarlo |= (0x80000000 >> entry); tlbarlo |= (0x80000000UL >> entry);
sysreg_write(TLBARLO, tlbarlo); sysreg_write(TLBARLO, tlbarlo);
/* update the entry with valid bit clear */ /* update the entry with valid bit clear */
asm volatile("tlbw"); __builtin_tlbw();
cpu_sync_pipeline();
} }
} }
...@@ -190,17 +181,22 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, ...@@ -190,17 +181,22 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
local_irq_save(flags); local_irq_save(flags);
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
mm->context = NO_CONTEXT; mm->context = NO_CONTEXT;
if (mm == current->mm) if (mm == current->mm)
activate_context(mm); activate_context(mm);
} else { } else {
unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK; unsigned long asid;
unsigned long saved_asid = MMU_NO_ASID; unsigned long saved_asid;
asid = mm->context & MMU_CONTEXT_ASID_MASK;
saved_asid = MMU_NO_ASID;
start &= PAGE_MASK; start &= PAGE_MASK;
end += (PAGE_SIZE - 1); end += (PAGE_SIZE - 1);
end &= PAGE_MASK; end &= PAGE_MASK;
if (mm != current->mm) { if (mm != current->mm) {
saved_asid = get_asid(); saved_asid = get_asid();
set_asid(asid); set_asid(asid);
...@@ -218,33 +214,34 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, ...@@ -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 * This function depends on the pages to be flushed having the G
* skip the ASID stuff and just use the Global bit... * (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) void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{ {
unsigned long flags; unsigned long flags;
int size; int size;
local_irq_save(flags);
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
flush_tlb_all(); flush_tlb_all();
} else { } else {
unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK; unsigned long asid;
unsigned long saved_asid = get_asid();
local_irq_save(flags);
asid = get_asid();
start &= PAGE_MASK; start &= PAGE_MASK;
end += (PAGE_SIZE - 1); end += (PAGE_SIZE - 1);
end &= PAGE_MASK; end &= PAGE_MASK;
set_asid(asid);
while (start < end) { while (start < end) {
__flush_tlb_page(asid, start); __flush_tlb_page(asid, start);
start += PAGE_SIZE; start += PAGE_SIZE;
} }
set_asid(saved_asid); local_irq_restore(flags);
} }
local_irq_restore(flags);
} }
void flush_tlb_mm(struct mm_struct *mm) void flush_tlb_mm(struct mm_struct *mm)
...@@ -280,7 +277,7 @@ static void *tlb_start(struct seq_file *tlb, loff_t *pos) ...@@ -280,7 +277,7 @@ static void *tlb_start(struct seq_file *tlb, loff_t *pos)
{ {
static unsigned long tlb_index; static unsigned long tlb_index;
if (*pos >= 32) if (*pos >= NR_TLB_ENTRIES)
return NULL; return NULL;
tlb_index = 0; tlb_index = 0;
...@@ -291,7 +288,7 @@ static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos) ...@@ -291,7 +288,7 @@ static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
{ {
unsigned long *index = v; unsigned long *index = v;
if (*index >= 31) if (*index >= NR_TLB_ENTRIES - 1)
return NULL; return NULL;
++*pos; ++*pos;
...@@ -313,16 +310,16 @@ static int tlb_show(struct seq_file *tlb, void *v) ...@@ -313,16 +310,16 @@ static int tlb_show(struct seq_file *tlb, void *v)
if (*index == 0) if (*index == 0)
seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n"); 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); local_irq_save(flags);
mmucr_save = sysreg_read(MMUCR); mmucr_save = sysreg_read(MMUCR);
tlbehi_save = sysreg_read(TLBEHI); tlbehi_save = sysreg_read(TLBEHI);
mmucr = mmucr_save & 0x13; mmucr = SYSREG_BFINS(DRP, *index, mmucr_save);
mmucr |= *index << 14;
sysreg_write(MMUCR, mmucr); sysreg_write(MMUCR, mmucr);
asm volatile("tlbr" : : : "memory"); /* TLBR might change the ASID */
__builtin_tlbr();
cpu_sync_pipeline(); cpu_sync_pipeline();
tlbehi = sysreg_read(TLBEHI); tlbehi = sysreg_read(TLBEHI);
...@@ -334,16 +331,18 @@ static int tlb_show(struct seq_file *tlb, void *v) ...@@ -334,16 +331,18 @@ static int tlb_show(struct seq_file *tlb, void *v)
local_irq_restore(flags); local_irq_restore(flags);
seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n", seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
*index, *index,
(tlbehi & 0x200)?'1':'0', SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
(tlbelo & 0x100)?'1':'0', SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
(tlbehi & 0xff), SYSREG_BFEXT(ASID, tlbehi),
(tlbehi >> 12), (tlbelo >> 12), SYSREG_BFEXT(VPN, tlbehi) >> 2,
(tlbelo >> 4) & 7, (tlbelo >> 2) & 3, SYSREG_BFEXT(PFN, tlbelo) >> 2,
(tlbelo & 0x200)?'1':'0', SYSREG_BFEXT(AP, tlbelo),
(tlbelo & 0x080)?'1':'0', SYSREG_BFEXT(SZ, tlbelo),
(tlbelo & 0x001)?'1':'0', SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '0',
(tlbelo & 0x002)?'1':'0'); SYSREG_BFEXT(B, tlbelo) ? '1' : '0',
SYSREG_BFEXT(W, tlbelo) ? '1' : '0',
SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '0');
return 0; return 0;
} }
......
...@@ -332,7 +332,7 @@ static int __init pwm_probe(struct platform_device *pdev) ...@@ -332,7 +332,7 @@ static int __init pwm_probe(struct platform_device *pdev)
p->base = ioremap(r->start, r->end - r->start + 1); p->base = ioremap(r->start, r->end - r->start + 1);
if (!p->base) if (!p->base)
goto fail; goto fail;
p->clk = clk_get(&pdev->dev, "mck"); p->clk = clk_get(&pdev->dev, "pwm_clk");
if (IS_ERR(p->clk)) { if (IS_ERR(p->clk)) {
status = PTR_ERR(p->clk); status = PTR_ERR(p->clk);
p->clk = NULL; p->clk = NULL;
......
...@@ -1277,8 +1277,45 @@ static int __exit macb_remove(struct platform_device *pdev) ...@@ -1277,8 +1277,45 @@ static int __exit macb_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver macb_driver = {
.remove = __exit_p(macb_remove), .remove = __exit_p(macb_remove),
.suspend = macb_suspend,
.resume = macb_resume,
.driver = { .driver = {
.name = "macb", .name = "macb",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -265,6 +265,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) ...@@ -265,6 +265,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
} }
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
device_init_wakeup(&pdev->dev, 1);
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
(unsigned long)rtc->regs, rtc->irq); (unsigned long)rtc->regs, rtc->irq);
...@@ -284,6 +285,8 @@ static int __exit at32_rtc_remove(struct platform_device *pdev) ...@@ -284,6 +285,8 @@ static int __exit at32_rtc_remove(struct platform_device *pdev)
{ {
struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev); struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
free_irq(rtc->irq, rtc); free_irq(rtc->irq, rtc);
iounmap(rtc->regs); iounmap(rtc->regs);
rtc_device_unregister(rtc->rtc); rtc_device_unregister(rtc->rtc);
......
...@@ -1439,14 +1439,29 @@ static struct uart_driver atmel_uart = { ...@@ -1439,14 +1439,29 @@ static struct uart_driver atmel_uart = {
}; };
#ifdef CONFIG_PM #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, static int atmel_serial_suspend(struct platform_device *pdev,
pm_message_t state) pm_message_t state)
{ {
struct uart_port *port = platform_get_drvdata(pdev); struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); 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) if (device_may_wakeup(&pdev->dev)
&& !at91_suspend_entering_slow_clock()) && !atmel_serial_clk_will_stop())
enable_irq_wake(port->irq); enable_irq_wake(port->irq);
else { else {
uart_suspend_port(&atmel_uart, port); uart_suspend_port(&atmel_uart, port);
......
...@@ -8,6 +8,12 @@ ...@@ -8,6 +8,12 @@
#define GPIO_PIN_NONE (-1) #define GPIO_PIN_NONE (-1)
/*
* Clock rates for various on-board oscillators. The number of entries
* in this array is chip-dependent.
*/
extern unsigned long at32_board_osc_rates[];
/* Add basic devices: system manager, interrupt controller, portmuxes, etc. */ /* Add basic devices: system manager, interrupt controller, portmuxes, etc. */
void at32_add_system_devices(void); void at32_add_system_devices(void);
...@@ -36,7 +42,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n); ...@@ -36,7 +42,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n);
struct atmel_lcdfb_info; struct atmel_lcdfb_info;
struct platform_device * struct platform_device *
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, 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 usba_platform_data; struct usba_platform_data;
struct platform_device * struct platform_device *
...@@ -73,6 +80,7 @@ struct platform_device *at32_add_device_twi(unsigned int id, ...@@ -73,6 +80,7 @@ struct platform_device *at32_add_device_twi(unsigned int id,
struct platform_device *at32_add_device_mci(unsigned int id); struct platform_device *at32_add_device_mci(unsigned int id);
struct platform_device *at32_add_device_ac97c(unsigned int id); struct platform_device *at32_add_device_ac97c(unsigned int id);
struct platform_device *at32_add_device_abdac(unsigned int id); struct platform_device *at32_add_device_abdac(unsigned int id);
struct platform_device *at32_add_device_psif(unsigned int id);
struct cf_platform_data { struct cf_platform_data {
int detect_pin; int detect_pin;
......
...@@ -13,10 +13,6 @@ ...@@ -13,10 +13,6 @@
void setup_platform(void); void setup_platform(void);
void setup_board(void); void setup_board(void);
/* Called by setup_platform */
void at32_clock_init(void);
void at32_portmux_init(void);
void at32_setup_serial_console(unsigned int usart_id); void at32_setup_serial_console(unsigned int usart_id);
#endif /* __ASM_AVR32_AT32AP_INIT_H__ */ #endif /* __ASM_AVR32_AT32AP_INIT_H__ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
extern void cpu_enter_idle(void); extern void cpu_enter_idle(void);
extern void cpu_enter_standby(unsigned long sdramc_base);
extern bool disable_idle_sleep; extern bool disable_idle_sleep;
...@@ -43,6 +44,8 @@ static inline void cpu_idle_sleep(void) ...@@ -43,6 +44,8 @@ static inline void cpu_idle_sleep(void)
else else
cpu_enter_idle(); cpu_enter_idle();
} }
void intc_set_suspend_handler(unsigned long offset);
#endif #endif
#endif /* __ASM_AVR32_ARCH_PM_H */ #endif /* __ASM_AVR32_ARCH_PM_H */
/*
* 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 */
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#define __ASM_AVR32_MMU_CONTEXT_H #define __ASM_AVR32_MMU_CONTEXT_H
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/pgalloc.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
#include <asm-generic/mm_hooks.h> #include <asm-generic/mm_hooks.h>
......
...@@ -5,4 +5,6 @@ ...@@ -5,4 +5,6 @@
#define PCI_DMA_BUS_IS_PHYS (1) #define PCI_DMA_BUS_IS_PHYS (1)
#include <asm-generic/pci-dma-compat.h>
#endif /* __ASM_AVR32_PCI_H__ */ #endif /* __ASM_AVR32_PCI_H__ */
...@@ -8,65 +8,79 @@ ...@@ -8,65 +8,79 @@
#ifndef __ASM_AVR32_PGALLOC_H #ifndef __ASM_AVR32_PGALLOC_H
#define __ASM_AVR32_PGALLOC_H #define __ASM_AVR32_PGALLOC_H
#include <asm/processor.h> #include <linux/quicklist.h>
#include <linux/threads.h> #include <asm/page.h>
#include <linux/slab.h> #include <asm/pgtable.h>
#include <linux/mm.h>
#define pmd_populate_kernel(mm, pmd, pte) \ #define QUICK_PGD 0 /* Preserve kernel mappings over free */
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) #define QUICK_PT 1 /* Zero on free */
static __inline__ void pmd_populate(struct mm_struct *mm, pmd_t *pmd, static inline void pmd_populate_kernel(struct mm_struct *mm,
pmd_t *pmd, pte_t *pte)
{
set_pmd(pmd, __pmd((unsigned long)pte));
}
static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
pgtable_t pte) pgtable_t pte)
{ {
set_pmd(pmd, __pmd(_PAGE_TABLE + page_to_phys(pte))); set_pmd(pmd, __pmd((unsigned long)page_address(pte)));
} }
#define pmd_pgtable(pmd) pmd_page(pmd) #define pmd_pgtable(pmd) pmd_page(pmd)
static inline void pgd_ctor(void *x)
{
pgd_t *pgd = x;
memcpy(pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
}
/* /*
* Allocate and free page tables * Allocate and free page tables
*/ */
static __inline__ pgd_t *pgd_alloc(struct mm_struct *mm) static inline pgd_t *pgd_alloc(struct mm_struct *mm)
{ {
return kcalloc(USER_PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL); return quicklist_alloc(QUICK_PGD, GFP_KERNEL | __GFP_REPEAT, pgd_ctor);
} }
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{ {
kfree(pgd); quicklist_free(QUICK_PGD, NULL, pgd);
} }
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
unsigned long address) unsigned long address)
{ {
pte_t *pte; return quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
return pte;
} }
static inline struct page *pte_alloc_one(struct mm_struct *mm, static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
unsigned long address) unsigned long address)
{ {
struct page *pte; struct page *page;
void *pg;
pte = alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO); pg = quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
if (!pte) if (!pg)
return NULL; return NULL;
pgtable_page_ctor(pte);
return pte; page = virt_to_page(pg);
pgtable_page_ctor(page);
return page;
} }
static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
{ {
free_page((unsigned long)pte); quicklist_free(QUICK_PT, NULL, pte);
} }
static inline void pte_free(struct mm_struct *mm, pgtable_t pte) static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
{ {
pgtable_page_dtor(pte); pgtable_page_dtor(pte);
__free_page(pte); quicklist_free_page(QUICK_PT, NULL, pte);
} }
#define __pte_free_tlb(tlb,pte) \ #define __pte_free_tlb(tlb,pte) \
...@@ -75,6 +89,10 @@ do { \ ...@@ -75,6 +89,10 @@ do { \
tlb_remove_page((tlb), pte); \ tlb_remove_page((tlb), pte); \
} while (0) } while (0)
#define check_pgt_cache() do { } while(0) static inline void check_pgt_cache(void)
{
quicklist_trim(QUICK_PGD, NULL, 25, 16);
quicklist_trim(QUICK_PT, NULL, 25, 16);
}
#endif /* __ASM_AVR32_PGALLOC_H */ #endif /* __ASM_AVR32_PGALLOC_H */
...@@ -129,13 +129,6 @@ extern struct page *empty_zero_page; ...@@ -129,13 +129,6 @@ extern struct page *empty_zero_page;
#define _PAGE_FLAGS_CACHE_MASK (_PAGE_CACHABLE | _PAGE_BUFFER | _PAGE_WT) #define _PAGE_FLAGS_CACHE_MASK (_PAGE_CACHABLE | _PAGE_BUFFER | _PAGE_WT)
/* TODO: Check for saneness */
/* User-mode page table flags (to be set in a pgd or pmd entry) */
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \
| _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
/* Kernel-mode page table flags */
#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \
| _PAGE_ACCESSED | _PAGE_DIRTY)
/* Flags that may be modified by software */ /* Flags that may be modified by software */
#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY \ #define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY \
| _PAGE_FLAGS_CACHE_MASK) | _PAGE_FLAGS_CACHE_MASK)
...@@ -262,10 +255,14 @@ static inline pte_t pte_mkspecial(pte_t pte) ...@@ -262,10 +255,14 @@ static inline pte_t pte_mkspecial(pte_t pte)
} }
#define pmd_none(x) (!pmd_val(x)) #define pmd_none(x) (!pmd_val(x))
#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) #define pmd_present(x) (pmd_val(x))
#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0)
#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) \ static inline void pmd_clear(pmd_t *pmdp)
!= _KERNPG_TABLE) {
set_pmd(pmdp, __pmd(0));
}
#define pmd_bad(x) (pmd_val(x) & ~PAGE_MASK)
/* /*
* Permanent address of a page. We don't support highmem, so this is * Permanent address of a page. We don't support highmem, so this is
...@@ -303,19 +300,16 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) ...@@ -303,19 +300,16 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
#define page_pte(page) page_pte_prot(page, __pgprot(0)) #define page_pte(page) page_pte_prot(page, __pgprot(0))
#define pmd_page_vaddr(pmd) \ #define pmd_page_vaddr(pmd) pmd_val(pmd)
((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) #define pmd_page(pmd) (virt_to_page(pmd_val(pmd)))
#define pmd_page(pmd) (phys_to_page(pmd_val(pmd)))
/* to find an entry in a page-table-directory. */ /* to find an entry in a page-table-directory. */
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) #define pgd_index(address) (((address) >> PGDIR_SHIFT) \
#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address)) & (PTRS_PER_PGD - 1))
#define pgd_offset_current(address) \ #define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
((pgd_t *)__mfsr(SYSREG_PTBR) + pgd_index(address))
/* to find an entry in a kernel page-table-directory */ /* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(address) pgd_offset(&init_mm, address) #define pgd_offset_k(address) pgd_offset(&init_mm, address)
/* Find an entry in the third-level page table.. */ /* Find an entry in the third-level page table.. */
#define pte_index(address) \ #define pte_index(address) \
......
...@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void) ...@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_MEMDIE 6 #define TIF_MEMDIE 6
#define TIF_RESTORE_SIGMASK 7 /* restore signal mask in do_signal */ #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_CPU_GOING_TO_SLEEP 8 /* CPU is entering sleep 0 mode */
#define TIF_FREEZE 29
#define TIF_DEBUG 30 /* debugging enabled */ #define TIF_DEBUG 30 /* debugging enabled */
#define TIF_USERSPACE 31 /* true if FS sets userspace */ #define TIF_USERSPACE 31 /* true if FS sets userspace */
......
...@@ -26,7 +26,6 @@ extern void flush_tlb_mm(struct mm_struct *mm); ...@@ -26,7 +26,6 @@ extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end); unsigned long end);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page); extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
extern void __flush_tlb_page(unsigned long asid, unsigned long page);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
......
...@@ -199,7 +199,7 @@ config BOUNCE ...@@ -199,7 +199,7 @@ config BOUNCE
config NR_QUICK config NR_QUICK
int int
depends on QUICKLIST depends on QUICKLIST
default "2" if SUPERH default "2" if SUPERH || AVR32
default "1" default "1"
config VIRT_TO_BUS config VIRT_TO_BUS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment