提交 6c118e43 编写于 作者: L 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
  ...
......@@ -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"
......
......@@ -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>
......@@ -25,6 +27,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;
......@@ -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)
{
unsigned i;
......@@ -165,12 +178,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);
......@@ -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];
......@@ -232,7 +238,7 @@ static int __init atstk1002_init(void)
set_hw_addr(at32_add_device_eth(1, &eth_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
......
......@@ -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,
......
......@@ -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);
......
......@@ -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
......
......@@ -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);
......
......@@ -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>
......
......@@ -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
......@@ -107,6 +99,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)
......
......@@ -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
......
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
......@@ -20,6 +20,7 @@
#include <asm/arch/at32ap700x.h>
#include <asm/arch/board.h>
#include <asm/arch/portmux.h>
#include <asm/arch/sram.h>
#include <video/atmel_lcdc.h>
......@@ -93,19 +94,12 @@ static struct clk devname##_##_name = { \
static DEFINE_SPINLOCK(pm_lock);
unsigned long at32ap7000_osc_rates[3] = {
[0] = 32768,
/* FIXME: these are ATSTK1002-specific */
[1] = 20000000,
[2] = 12000000,
};
static struct clk osc0;
static struct clk osc1;
static unsigned long osc_get_rate(struct clk *clk)
{
return at32ap7000_osc_rates[clk->index];
return at32_board_osc_rates[clk->index];
}
static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
......@@ -682,6 +676,14 @@ static struct clk hramc_clk = {
.users = 1,
.index = 3,
};
static struct clk sdramc_clk = {
.name = "sdramc_clk",
.parent = &pbb_clk,
.mode = pbb_clk_mode,
.get_rate = pbb_clk_get_rate,
.users = 1,
.index = 14,
};
static struct resource smc0_resource[] = {
PBMEM(0xfff03400),
......@@ -840,6 +842,81 @@ void __init at32_add_system_devices(void)
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
* -------------------------------------------------------------------- */
......@@ -1113,7 +1190,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
switch (id) {
case 0:
pdev = &atmel_spi0_device;
select_peripheral(PA(0), PERIPH_A, 0); /* MISO */
/* pullup MISO so a level is always defined */
select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP);
select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */
select_peripheral(PA(2), PERIPH_A, 0); /* SCK */
at32_spi_setup_slaves(0, b, n, spi0_pins);
......@@ -1121,7 +1199,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
case 1:
pdev = &atmel_spi1_device;
select_peripheral(PB(0), PERIPH_B, 0); /* MISO */
/* pullup MISO so a level is always defined */
select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP);
select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */
select_peripheral(PB(5), PERIPH_B, 0); /* SCK */
at32_spi_setup_slaves(1, b, n, spi1_pins);
......@@ -1264,7 +1343,8 @@ static struct clk atmel_lcdfb0_pixclk = {
struct platform_device *__init
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
unsigned long fbmem_start, unsigned long fbmem_len)
unsigned long fbmem_start, unsigned long fbmem_len,
unsigned int pin_config)
{
struct platform_device *pdev;
struct atmel_lcdfb_info *info;
......@@ -1291,6 +1371,9 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
switch (id) {
case 0:
pdev = &atmel_lcdfb0_device;
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 */
......@@ -1322,6 +1405,43 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */
select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
break;
case 1:
select_peripheral(PE(0), PERIPH_B, 0); /* CC */
select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */
select_peripheral(PE(2), PERIPH_B, 0); /* MODE */
select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */
select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */
select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */
select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */
select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */
select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */
select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */
select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */
select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */
select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */
select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */
select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */
select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */
select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */
select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */
select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */
select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
break;
default:
goto err_invalid_id;
}
clk_set_parent(&atmel_lcdfb0_pixclk, &pll0);
clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
......@@ -1360,7 +1480,7 @@ static struct resource atmel_pwm0_resource[] __initdata = {
IRQ(24),
};
static struct clk atmel_pwm0_mck = {
.name = "mck",
.name = "pwm_clk",
.parent = &pbb_clk,
.mode = pbb_clk_mode,
.get_rate = pbb_clk_get_rate,
......@@ -1887,6 +2007,7 @@ struct clk *at32_clock_list[] = {
&hmatrix_clk,
&ebi_clk,
&hramc_clk,
&sdramc_clk,
&smc0_pclk,
&smc0_mck,
&pdc_hclk,
......@@ -1900,6 +2021,8 @@ struct clk *at32_clock_list[] = {
&pio4_mck,
&at32_tcb0_t0_clk,
&at32_tcb1_t0_clk,
&atmel_psif0_pclk,
&atmel_psif1_pclk,
&atmel_usart0_usart,
&atmel_usart1_usart,
&atmel_usart2_usart,
......@@ -1935,16 +2058,7 @@ struct clk *at32_clock_list[] = {
};
unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
void __init at32_portmux_init(void)
{
at32_init_pio(&pio0_device);
at32_init_pio(&pio1_device);
at32_init_pio(&pio2_device);
at32_init_pio(&pio3_device);
at32_init_pio(&pio4_device);
}
void __init at32_clock_init(void)
void __init setup_platform(void)
{
u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
int i;
......@@ -1999,4 +2113,36 @@ void __init at32_clock_init(void)
pm_writel(HSB_MASK, hsb_mask);
pm_writel(PBA_MASK, pba_mask);
pm_writel(PBB_MASK, pbb_mask);
/* Initialize the port muxes */
at32_init_pio(&pio0_device);
at32_init_pio(&pio1_device);
at32_init_pio(&pio2_device);
at32_init_pio(&pio3_device);
at32_init_pio(&pio4_device);
}
struct gen_pool *sram_pool;
static int __init sram_init(void)
{
struct gen_pool *pool;
/* 1KiB granularity */
pool = gen_pool_create(10, -1);
if (!pool)
goto fail;
if (gen_pool_add(pool, 0x24000000, 0x8000, -1))
goto err_pool_add;
sram_pool = pool;
return 0;
err_pool_add:
gen_pool_destroy(pool);
fail:
pr_err("Failed to create SRAM pool\n");
return -ENOMEM;
}
core_initcall(sram_init);
/*
* 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,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <asm/io.h>
......@@ -20,6 +21,11 @@
struct intc {
void __iomem *regs;
struct irq_chip chip;
struct sys_device sysdev;
#ifdef CONFIG_PM
unsigned long suspend_ipr;
unsigned long saved_ipr[64];
#endif
};
extern struct platform_device at32_intc0_device;
......@@ -136,6 +142,74 @@ void __init init_IRQ(void)
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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -57,7 +57,7 @@
/* Bitfields in IFDR */
/* Bitfields in ISFR */
/* Bitfields in IFSR */
/* Bitfields in SODR */
......
......@@ -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 */
/*
* 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 @@
#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;
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);
......
......@@ -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);
}
}
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);
......@@ -335,15 +332,17 @@ static int tlb_show(struct seq_file *tlb, void *v)
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');
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;
}
......
......@@ -332,7 +332,7 @@ static int __init pwm_probe(struct platform_device *pdev)
p->base = ioremap(r->start, r->end - r->start + 1);
if (!p->base)
goto fail;
p->clk = clk_get(&pdev->dev, "mck");
p->clk = clk_get(&pdev->dev, "pwm_clk");
if (IS_ERR(p->clk)) {
status = PTR_ERR(p->clk);
p->clk = NULL;
......
......@@ -1277,8 +1277,45 @@ static int __exit macb_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int macb_suspend(struct platform_device *pdev, pm_message_t state)
{
struct net_device *netdev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(netdev);
netif_device_detach(netdev);
#ifndef CONFIG_ARCH_AT91
clk_disable(bp->hclk);
#endif
clk_disable(bp->pclk);
return 0;
}
static int macb_resume(struct platform_device *pdev)
{
struct net_device *netdev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(netdev);
clk_enable(bp->pclk);
#ifndef CONFIG_ARCH_AT91
clk_enable(bp->hclk);
#endif
netif_device_attach(netdev);
return 0;
}
#else
#define macb_suspend NULL
#define macb_resume NULL
#endif
static struct platform_driver macb_driver = {
.remove = __exit_p(macb_remove),
.suspend = macb_suspend,
.resume = macb_resume,
.driver = {
.name = "macb",
.owner = THIS_MODULE,
......
......@@ -265,6 +265,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, rtc);
device_init_wakeup(&pdev->dev, 1);
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
(unsigned long)rtc->regs, rtc->irq);
......@@ -284,6 +285,8 @@ static int __exit at32_rtc_remove(struct platform_device *pdev)
{
struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
free_irq(rtc->irq, rtc);
iounmap(rtc->regs);
rtc_device_unregister(rtc->rtc);
......
......@@ -1439,14 +1439,29 @@ static struct uart_driver atmel_uart = {
};
#ifdef CONFIG_PM
static bool atmel_serial_clk_will_stop(void)
{
#ifdef CONFIG_ARCH_AT91
return at91_suspend_entering_slow_clock();
#else
return false;
#endif
}
static int atmel_serial_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
if (atmel_is_console_port(port) && console_suspend_enabled) {
/* Drain the TX shifter */
while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
cpu_relax();
}
if (device_may_wakeup(&pdev->dev)
&& !at91_suspend_entering_slow_clock())
&& !atmel_serial_clk_will_stop())
enable_irq_wake(port->irq);
else {
uart_suspend_port(&atmel_uart, port);
......
......@@ -8,6 +8,12 @@
#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. */
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);
struct atmel_lcdfb_info;
struct platform_device *
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 platform_device *
......@@ -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_ac97c(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 {
int detect_pin;
......
......@@ -13,10 +13,6 @@
void setup_platform(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);
#endif /* __ASM_AVR32_AT32AP_INIT_H__ */
......@@ -19,6 +19,7 @@
#ifndef __ASSEMBLY__
extern void cpu_enter_idle(void);
extern void cpu_enter_standby(unsigned long sdramc_base);
extern bool disable_idle_sleep;
......@@ -43,6 +44,8 @@ static inline void cpu_idle_sleep(void)
else
cpu_enter_idle();
}
void intc_set_suspend_handler(unsigned long offset);
#endif
#endif /* __ASM_AVR32_ARCH_PM_H */
/*
* 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 @@
#define __ASM_AVR32_MMU_CONTEXT_H
#include <asm/tlbflush.h>
#include <asm/pgalloc.h>
#include <asm/sysreg.h>
#include <asm-generic/mm_hooks.h>
......
......@@ -5,4 +5,6 @@
#define PCI_DMA_BUS_IS_PHYS (1)
#include <asm-generic/pci-dma-compat.h>
#endif /* __ASM_AVR32_PCI_H__ */
......@@ -8,65 +8,79 @@
#ifndef __ASM_AVR32_PGALLOC_H
#define __ASM_AVR32_PGALLOC_H
#include <asm/processor.h>
#include <linux/threads.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/quicklist.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#define pmd_populate_kernel(mm, pmd, pte) \
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)))
#define QUICK_PGD 0 /* Preserve kernel mappings over free */
#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)
{
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)
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
*/
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)
{
kfree(pgd);
quicklist_free(QUICK_PGD, NULL, pgd);
}
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
unsigned long address)
{
pte_t *pte;
pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
return pte;
return quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
}
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)
{
struct page *pte;
struct page *page;
void *pg;
pte = alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
if (!pte)
pg = quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
if (!pg)
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)
{
free_page((unsigned long)pte);
quicklist_free(QUICK_PT, NULL, pte);
}
static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
{
pgtable_page_dtor(pte);
__free_page(pte);
quicklist_free_page(QUICK_PT, NULL, pte);
}
#define __pte_free_tlb(tlb,pte) \
......@@ -75,6 +89,10 @@ do { \
tlb_remove_page((tlb), pte); \
} 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 */
......@@ -129,13 +129,6 @@ extern struct page *empty_zero_page;
#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 */
#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY \
| _PAGE_FLAGS_CACHE_MASK)
......@@ -262,10 +255,14 @@ static inline pte_t pte_mkspecial(pte_t pte)
}
#define pmd_none(x) (!pmd_val(x))
#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0)
#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) \
!= _KERNPG_TABLE)
#define pmd_present(x) (pmd_val(x))
static inline void pmd_clear(pmd_t *pmdp)
{
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
......@@ -303,16 +300,13 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
#define page_pte(page) page_pte_prot(page, __pgprot(0))
#define pmd_page_vaddr(pmd) \
((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
#define pmd_page(pmd) (phys_to_page(pmd_val(pmd)))
#define pmd_page_vaddr(pmd) pmd_val(pmd)
#define pmd_page(pmd) (virt_to_page(pmd_val(pmd)))
/* to find an entry in a page-table-directory. */
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
#define pgd_offset_current(address) \
((pgd_t *)__mfsr(SYSREG_PTBR) + pgd_index(address))
#define pgd_index(address) (((address) >> PGDIR_SHIFT) \
& (PTRS_PER_PGD - 1))
#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
......
......@@ -88,6 +88,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_MEMDIE 6
#define TIF_RESTORE_SIGMASK 7 /* restore signal mask in do_signal */
#define TIF_CPU_GOING_TO_SLEEP 8 /* CPU is entering sleep 0 mode */
#define TIF_FREEZE 29
#define TIF_DEBUG 30 /* debugging enabled */
#define TIF_USERSPACE 31 /* true if FS sets userspace */
......
......@@ -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,
unsigned long end);
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);
......
......@@ -199,7 +199,7 @@ config BOUNCE
config NR_QUICK
int
depends on QUICKLIST
default "2" if SUPERH
default "2" if SUPERH || AVR32
default "1"
config VIRT_TO_BUS
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册