aboutsummaryrefslogtreecommitdiff
path: root/arch/avr32/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/avr32/kernel')
-rw-r--r--arch/avr32/kernel/cpu.c64
-rw-r--r--arch/avr32/kernel/entry-avr32b.S124
-rw-r--r--arch/avr32/kernel/module.c11
-rw-r--r--arch/avr32/kernel/process.c193
-rw-r--r--arch/avr32/kernel/setup.c484
-rw-r--r--arch/avr32/kernel/time.c150
-rw-r--r--arch/avr32/kernel/traps.c421
-rw-r--r--arch/avr32/kernel/vmlinux.lds.c9
8 files changed, 892 insertions, 564 deletions
diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c
index 2e72fd2699d..2714cf6452b 100644
--- a/arch/avr32/kernel/cpu.c
+++ b/arch/avr32/kernel/cpu.c
@@ -209,16 +209,17 @@ static const char *mmu_types[] = {
void __init setup_processor(void)
{
unsigned long config0, config1;
+ unsigned long features;
unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type;
unsigned tmp;
- config0 = sysreg_read(CONFIG0); /* 0x0000013e; */
- config1 = sysreg_read(CONFIG1); /* 0x01f689a2; */
- cpu_id = config0 >> 24;
- cpu_rev = (config0 >> 16) & 0xff;
- arch_id = (config0 >> 13) & 0x07;
- arch_rev = (config0 >> 10) & 0x07;
- mmu_type = (config0 >> 7) & 0x03;
+ config0 = sysreg_read(CONFIG0);
+ config1 = sysreg_read(CONFIG1);
+ cpu_id = SYSREG_BFEXT(PROCESSORID, config0);
+ cpu_rev = SYSREG_BFEXT(PROCESSORREVISION, config0);
+ arch_id = SYSREG_BFEXT(AT, config0);
+ arch_rev = SYSREG_BFEXT(AR, config0);
+ mmu_type = SYSREG_BFEXT(MMUT, config0);
boot_cpu_data.arch_type = arch_id;
boot_cpu_data.cpu_type = cpu_id;
@@ -226,16 +227,16 @@ void __init setup_processor(void)
boot_cpu_data.cpu_revision = cpu_rev;
boot_cpu_data.tlb_config = mmu_type;
- tmp = (config1 >> 13) & 0x07;
+ tmp = SYSREG_BFEXT(ILSZ, config1);
if (tmp) {
- boot_cpu_data.icache.ways = 1 << ((config1 >> 10) & 0x07);
- boot_cpu_data.icache.sets = 1 << ((config1 >> 16) & 0x0f);
+ boot_cpu_data.icache.ways = 1 << SYSREG_BFEXT(IASS, config1);
+ boot_cpu_data.icache.sets = 1 << SYSREG_BFEXT(ISET, config1);
boot_cpu_data.icache.linesz = 1 << (tmp + 1);
}
- tmp = (config1 >> 3) & 0x07;
+ tmp = SYSREG_BFEXT(DLSZ, config1);
if (tmp) {
- boot_cpu_data.dcache.ways = 1 << (config1 & 0x07);
- boot_cpu_data.dcache.sets = 1 << ((config1 >> 6) & 0x0f);
+ boot_cpu_data.dcache.ways = 1 << SYSREG_BFEXT(DASS, config1);
+ boot_cpu_data.dcache.sets = 1 << SYSREG_BFEXT(DSET, config1);
boot_cpu_data.dcache.linesz = 1 << (tmp + 1);
}
@@ -250,16 +251,39 @@ void __init setup_processor(void)
cpu_names[cpu_id], cpu_id, cpu_rev,
arch_names[arch_id], arch_rev);
printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]);
+
printk ("CPU: features:");
- if (config0 & (1 << 6))
- printk(" fpu");
- if (config0 & (1 << 5))
- printk(" java");
- if (config0 & (1 << 4))
- printk(" perfctr");
- if (config0 & (1 << 3))
+ features = 0;
+ if (config0 & SYSREG_BIT(CONFIG0_R)) {
+ features |= AVR32_FEATURE_RMW;
+ printk(" rmw");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_D)) {
+ features |= AVR32_FEATURE_DSP;
+ printk(" dsp");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_S)) {
+ features |= AVR32_FEATURE_SIMD;
+ printk(" simd");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_O)) {
+ features |= AVR32_FEATURE_OCD;
printk(" ocd");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_P)) {
+ features |= AVR32_FEATURE_PCTR;
+ printk(" perfctr");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_J)) {
+ features |= AVR32_FEATURE_JAVA;
+ printk(" java");
+ }
+ if (config0 & SYSREG_BIT(CONFIG0_F)) {
+ features |= AVR32_FEATURE_FPU;
+ printk(" fpu");
+ }
printk("\n");
+ boot_cpu_data.features = features;
}
#ifdef CONFIG_PROC_FS
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index eeb66792bc3..42657f1703b 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -100,55 +100,49 @@ dtlb_miss_write:
.global tlb_miss_common
tlb_miss_common:
- mfsr r0, SYSREG_PTBR
- mfsr r1, SYSREG_TLBEAR
+ mfsr r0, SYSREG_TLBEAR
+ mfsr r1, SYSREG_PTBR
/* Is it the vmalloc space? */
- bld r1, 31
+ bld r0, 31
brcs handle_vmalloc_miss
/* First level lookup */
pgtbl_lookup:
- lsr r2, r1, PGDIR_SHIFT
- ld.w r0, r0[r2 << 2]
- bld r0, _PAGE_BIT_PRESENT
+ 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
- /* TODO: Check access rights on page table if necessary */
-
/* Translate to virtual address in P1. */
- andl r0, 0xf000
- sbr r0, 31
+ andl r3, 0xf000
+ sbr r3, 31
/* Second level lookup */
- lsl r1, (32 - PGDIR_SHIFT)
- lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
- add r2, r0, r1 << 2
- ld.w r1, r2[0]
- bld r1, _PAGE_BIT_PRESENT
+ ld.w r2, r3[r1 << 2]
+ mfsr r0, SYSREG_TLBARLO
+ bld r2, _PAGE_BIT_PRESENT
brcc page_not_present
/* Mark the page as accessed */
- sbr r1, _PAGE_BIT_ACCESSED
- st.w r2[0], r1
+ sbr r2, _PAGE_BIT_ACCESSED
+ st.w r3[r1 << 2], r2
/* Drop software flags */
- andl r1, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
- mtsr SYSREG_TLBELO, r1
+ andl r2, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
+ mtsr SYSREG_TLBELO, r2
/* Figure out which entry we want to replace */
- mfsr r0, SYSREG_TLBARLO
+ mfsr r1, SYSREG_MMUCR
clz r2, r0
brcc 1f
- mov r1, -1 /* All entries have been accessed, */
- mtsr SYSREG_TLBARLO, r1 /* so reset TLBAR */
- mov r2, 0 /* and start at 0 */
-1: mfsr r1, SYSREG_MMUCR
- lsl r2, 14
- andl r1, 0x3fff, COH
- or r1, r2
- mtsr SYSREG_MMUCR, r1
+ mov r3, -1 /* All entries have been accessed, */
+ mov r2, 0 /* so start at 0 */
+ mtsr SYSREG_TLBARLO, r3 /* and reset TLBAR */
+1: bfins r1, r2, SYSREG_DRP_OFFSET, SYSREG_DRP_SIZE
+ mtsr SYSREG_MMUCR, r1
tlbw
tlbmiss_restore
@@ -156,8 +150,8 @@ pgtbl_lookup:
handle_vmalloc_miss:
/* Simply do the lookup in init's page table */
- mov r0, lo(swapper_pg_dir)
- orh r0, hi(swapper_pg_dir)
+ mov r1, lo(swapper_pg_dir)
+ orh r1, hi(swapper_pg_dir)
rjmp pgtbl_lookup
@@ -340,12 +334,34 @@ do_bus_error_read:
do_nmi_ll:
sub sp, 4
stmts --sp, r0-lr
- /* FIXME: Make sure RAR_NMI and RSR_NMI are pushed instead of *_EX */
- rcall save_full_context_ex
+ mfsr r9, SYSREG_RSR_NMI
+ mfsr r8, SYSREG_RAR_NMI
+ bfextu r0, r9, MODE_SHIFT, 3
+ brne 2f
+
+1: pushm r8, r9 /* PC and SR */
mfsr r12, SYSREG_ECR
mov r11, sp
rcall do_nmi
- rjmp bad_return
+ popm r8-r9
+ mtsr SYSREG_RAR_NMI, r8
+ tst r0, r0
+ mtsr SYSREG_RSR_NMI, r9
+ brne 3f
+
+ ldmts sp++, r0-lr
+ sub sp, -4 /* skip r12_orig */
+ rete
+
+2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR)
+ stdsp sp[4], r10 /* replace saved SP */
+ rjmp 1b
+
+3: popm lr
+ sub sp, -4 /* skip sp */
+ popm r0-r12
+ sub sp, -4 /* skip r12_orig */
+ rete
handle_address_fault:
sub sp, 4
@@ -630,9 +646,12 @@ irq_level\level:
rcall do_IRQ
lddsp r4, sp[REG_SR]
- andh r4, (MODE_MASK >> 16), COH
+ bfextu r4, r4, SYSREG_M0_OFFSET, 3
+ cp.w r4, MODE_SUPERVISOR >> SYSREG_M0_OFFSET
+ breq 2f
+ cp.w r4, MODE_USER >> SYSREG_M0_OFFSET
#ifdef CONFIG_PREEMPT
- brne 2f
+ brne 3f
#else
brne 1f
#endif
@@ -649,9 +668,18 @@ irq_level\level:
sub sp, -4 /* ignore r12_orig */
rete
+2: get_thread_info r0
+ ld.w r1, r0[TI_flags]
+ bld r1, TIF_CPU_GOING_TO_SLEEP
#ifdef CONFIG_PREEMPT
-2:
- get_thread_info r0
+ brcc 3f
+#else
+ brcc 1b
+#endif
+ sub r1, pc, . - cpu_idle_skip_sleep
+ stdsp sp[REG_PC], r1
+#ifdef CONFIG_PREEMPT
+3: get_thread_info r0
ld.w r2, r0[TI_preempt_count]
cp.w r2, 0
brne 1b
@@ -662,12 +690,32 @@ irq_level\level:
bld r4, SYSREG_GM_OFFSET
brcs 1b
rcall preempt_schedule_irq
- rjmp 1b
#endif
+ rjmp 1b
.endm
.section .irq.text,"ax",@progbits
+.global cpu_idle_sleep
+cpu_idle_sleep:
+ mask_interrupts
+ get_thread_info r8
+ ld.w r9, r8[TI_flags]
+ bld r9, TIF_NEED_RESCHED
+ brcs cpu_idle_enable_int_and_exit
+ sbr r9, TIF_CPU_GOING_TO_SLEEP
+ st.w r8[TI_flags], r9
+ unmask_interrupts
+ sleep 0
+cpu_idle_skip_sleep:
+ mask_interrupts
+ ld.w r9, r8[TI_flags]
+ cbr r9, TIF_CPU_GOING_TO_SLEEP
+ st.w r8[TI_flags], r9
+cpu_idle_enable_int_and_exit:
+ unmask_interrupts
+ retal r12
+
.global irq_level0
.global irq_level1
.global irq_level2
diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c
index b599eae6457..1167fe9cf6c 100644
--- a/arch/avr32/kernel/module.c
+++ b/arch/avr32/kernel/module.c
@@ -12,10 +12,11 @@
* published by the Free Software Foundation.
*/
-#include <linux/moduleloader.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/bug.h>
#include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleloader.h>
#include <linux/vmalloc.h>
void *module_alloc(unsigned long size)
@@ -315,10 +316,10 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
vfree(module->arch.syminfo);
module->arch.syminfo = NULL;
- return 0;
+ return module_bug_finalize(hdr, sechdrs, module);
}
void module_arch_cleanup(struct module *module)
{
-
+ module_bug_cleanup(module);
}
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 0b4325946a4..4e4181ed1c6 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -11,6 +11,7 @@
#include <linux/fs.h>
#include <linux/ptrace.h>
#include <linux/reboot.h>
+#include <linux/uaccess.h>
#include <linux/unistd.h>
#include <asm/sysreg.h>
@@ -19,6 +20,8 @@
void (*pm_power_off)(void) = NULL;
EXPORT_SYMBOL(pm_power_off);
+extern void cpu_idle_sleep(void);
+
/*
* This file handles the architecture-dependent parts of process handling..
*/
@@ -27,9 +30,8 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
- /* TODO: Enter sleep mode */
while (!need_resched())
- cpu_relax();
+ cpu_idle_sleep();
preempt_enable_no_resched();
schedule();
preempt_disable();
@@ -114,39 +116,178 @@ void release_thread(struct task_struct *dead_task)
/* do nothing */
}
+static void dump_mem(const char *str, const char *log_lvl,
+ unsigned long bottom, unsigned long top)
+{
+ unsigned long p;
+ int i;
+
+ printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top);
+
+ for (p = bottom & ~31; p < top; ) {
+ printk("%s%04lx: ", log_lvl, p & 0xffff);
+
+ for (i = 0; i < 8; i++, p += 4) {
+ unsigned int val;
+
+ if (p < bottom || p >= top)
+ printk(" ");
+ else {
+ if (__get_user(val, (unsigned int __user *)p)) {
+ printk("\n");
+ goto out;
+ }
+ printk("%08x ", val);
+ }
+ }
+ printk("\n");
+ }
+
+out:
+ return;
+}
+
+static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
+{
+ return (p > (unsigned long)tinfo)
+ && (p < (unsigned long)tinfo + THREAD_SIZE - 3);
+}
+
+#ifdef CONFIG_FRAME_POINTER
+static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs, const char *log_lvl)
+{
+ unsigned long lr, fp;
+ struct thread_info *tinfo;
+
+ if (regs)
+ fp = regs->r7;
+ else if (tsk == current)
+ asm("mov %0, r7" : "=r"(fp));
+ else
+ fp = tsk->thread.cpu_context.r7;
+
+ /*
+ * Walk the stack as long as the frame pointer (a) is within
+ * the kernel stack of the task, and (b) it doesn't move
+ * downwards.
+ */
+ tinfo = task_thread_info(tsk);
+ printk("%sCall trace:\n", log_lvl);
+ while (valid_stack_ptr(tinfo, fp)) {
+ unsigned long new_fp;
+
+ lr = *(unsigned long *)fp;
+#ifdef CONFIG_KALLSYMS
+ printk("%s [<%08lx>] ", log_lvl, lr);
+#else
+ printk(" [<%08lx>] ", lr);
+#endif
+ print_symbol("%s\n", lr);
+
+ new_fp = *(unsigned long *)(fp + 4);
+ if (new_fp <= fp)
+ break;
+ fp = new_fp;
+ }
+ printk("\n");
+}
+#else
+static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs, const char *log_lvl)
+{
+ unsigned long addr;
+
+ printk("%sCall trace:\n", log_lvl);
+
+ while (!kstack_end(sp)) {
+ addr = *sp++;
+ if (kernel_text_address(addr)) {
+#ifdef CONFIG_KALLSYMS
+ printk("%s [<%08lx>] ", log_lvl, addr);
+#else
+ printk(" [<%08lx>] ", addr);
+#endif
+ print_symbol("%s\n", addr);
+ }
+ }
+ printk("\n");
+}
+#endif
+
+void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
+ struct pt_regs *regs, const char *log_lvl)
+{
+ struct thread_info *tinfo;
+
+ if (sp == 0) {
+ if (tsk)
+ sp = tsk->thread.cpu_context.ksp;
+ else
+ sp = (unsigned long)&tinfo;
+ }
+ if (!tsk)
+ tsk = current;
+
+ tinfo = task_thread_info(tsk);
+
+ if (valid_stack_ptr(tinfo, sp)) {
+ dump_mem("Stack: ", log_lvl, sp,
+ THREAD_SIZE + (unsigned long)tinfo);
+ show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);
+ }
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *stack)
+{
+ show_stack_log_lvl(tsk, (unsigned long)stack, NULL, "");
+}
+
+void dump_stack(void)
+{
+ unsigned long stack;
+
+ show_trace_log_lvl(current, &stack, NULL, "");
+}
+EXPORT_SYMBOL(dump_stack);
+
static const char *cpu_modes[] = {
"Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
"Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
};
-void show_regs(struct pt_regs *regs)
+void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl)
{
unsigned long sp = regs->sp;
unsigned long lr = regs->lr;
unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
- if (!user_mode(regs))
+ if (!user_mode(regs)) {
sp = (unsigned long)regs + FRAME_SIZE_FULL;
- print_symbol("PC is at %s\n", instruction_pointer(regs));
- print_symbol("LR is at %s\n", lr);
- printk("pc : [<%08lx>] lr : [<%08lx>] %s\n"
- "sp : %08lx r12: %08lx r11: %08lx\n",
- instruction_pointer(regs),
- lr, print_tainted(), sp, regs->r12, regs->r11);
- printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
- regs->r10, regs->r9, regs->r8);
- printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
- regs->r7, regs->r6, regs->r5, regs->r4);
- printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
- regs->r3, regs->r2, regs->r1, regs->r0);
- printk("Flags: %c%c%c%c%c\n",
+ printk("%s", log_lvl);
+ print_symbol("PC is at %s\n", instruction_pointer(regs));
+ printk("%s", log_lvl);
+ print_symbol("LR is at %s\n", lr);
+ }
+
+ printk("%spc : [<%08lx>] lr : [<%08lx>] %s\n"
+ "%ssp : %08lx r12: %08lx r11: %08lx\n",
+ log_lvl, instruction_pointer(regs), lr, print_tainted(),
+ log_lvl, sp, regs->r12, regs->r11);
+ printk("%sr10: %08lx r9 : %08lx r8 : %08lx\n",
+ log_lvl, regs->r10, regs->r9, regs->r8);
+ printk("%sr7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
+ log_lvl, regs->r7, regs->r6, regs->r5, regs->r4);
+ printk("%sr3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
+ log_lvl, regs->r3, regs->r2, regs->r1, regs->r0);
+ printk("%sFlags: %c%c%c%c%c\n", log_lvl,
regs->sr & SR_Q ? 'Q' : 'q',
regs->sr & SR_V ? 'V' : 'v',
regs->sr & SR_N ? 'N' : 'n',
regs->sr & SR_Z ? 'Z' : 'z',
regs->sr & SR_C ? 'C' : 'c');
- printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
+ printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl,
regs->sr & SR_H ? 'H' : 'h',
regs->sr & SR_R ? 'R' : 'r',
regs->sr & SR_J ? 'J' : 'j',
@@ -156,9 +297,21 @@ void show_regs(struct pt_regs *regs)
regs->sr & SR_I1M ? '1' : '.',
regs->sr & SR_I0M ? '0' : '.',
regs->sr & SR_GM ? 'G' : 'g');
- printk("CPU Mode: %s\n", cpu_modes[mode]);
+ printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]);
+ printk("%sProcess: %s [%d] (task: %p thread: %p)\n",
+ log_lvl, current->comm, current->pid, current,
+ task_thread_info(current));
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ unsigned long sp = regs->sp;
+
+ if (!user_mode(regs))
+ sp = (unsigned long)regs + FRAME_SIZE_FULL;
- show_trace(NULL, (unsigned long *)sp, regs);
+ show_regs_log_lvl(regs, "");
+ show_trace_log_lvl(current, (unsigned long *)sp, regs, "");
}
EXPORT_SYMBOL(show_regs);
diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c
index a1a7c3c3f52..b279d66acf5 100644
--- a/arch/avr32/kernel/setup.c
+++ b/arch/avr32/kernel/setup.c
@@ -8,12 +8,14 @@
#include <linux/clk.h>
#include <linux/init.h>
+#include <linux/initrd.h>
#include <linux/sched.h>
#include <linux/console.h>
#include <linux/ioport.h>
#include <linux/bootmem.h>
#include <linux/fs.h>
#include <linux/module.h>
+#include <linux/pfn.h>
#include <linux/root_dev.h>
#include <linux/cpu.h>
#include <linux/kernel.h>
@@ -30,13 +32,6 @@
extern int root_mountflags;
/*
- * Bootloader-provided information about physical memory
- */
-struct tag_mem_range *mem_phys;
-struct tag_mem_range *mem_reserved;
-struct tag_mem_range *mem_ramdisk;
-
-/*
* Initialize loops_per_jiffy as 5000000 (500MIPS).
* Better make it too large than too small...
*/
@@ -48,48 +43,193 @@ EXPORT_SYMBOL(boot_cpu_data);
static char __initdata command_line[COMMAND_LINE_SIZE];
/*
- * Should be more than enough, but if you have a _really_ complex
- * setup, you might need to increase the size of this...
+ * Standard memory resources
*/
-static struct tag_mem_range __initdata mem_range_cache[32];
-static unsigned mem_range_next_free;
+static struct resource __initdata kernel_data = {
+ .name = "Kernel data",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM,
+};
+static struct resource __initdata kernel_code = {
+ .name = "Kernel code",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM,
+ .sibling = &kernel_data,
+};
/*
- * Standard memory resources
+ * Available system RAM and reserved regions as singly linked
+ * lists. These lists are traversed using the sibling pointer in
+ * struct resource and are kept sorted at all times.
*/
-static struct resource mem_res[] = {
- {
- .name = "Kernel code",
- .start = 0,
- .end = 0,
- .flags = IORESOURCE_MEM
- },
- {
- .name = "Kernel data",
- .start = 0,
- .end = 0,
- .flags = IORESOURCE_MEM,
- },
-};
+static struct resource *__initdata system_ram;
+static struct resource *__initdata reserved = &kernel_code;
+
+/*
+ * We need to allocate these before the bootmem allocator is up and
+ * running, so we need this "cache". 32 entries are probably enough
+ * for all but the most insanely complex systems.
+ */
+static struct resource __initdata res_cache[32];
+static unsigned int __initdata res_cache_next_free;
+
+static void __init resource_init(void)
+{
+ struct resource *mem, *res;
+ struct resource *new;
+
+ kernel_code.start = __pa(init_mm.start_code);
+
+ for (mem = system_ram; mem; mem = mem->sibling) {
+ new = alloc_bootmem_low(sizeof(struct resource));
+ memcpy(new, mem, sizeof(struct resource));
+
+ new->sibling = NULL;
+ if (request_resource(&iomem_resource, new))
+ printk(KERN_WARNING "Bad RAM resource %08x-%08x\n",
+ mem->start, mem->end);
+ }
+
+ for (res = reserved; res; res = res->sibling) {
+ new = alloc_bootmem_low(sizeof(struct resource));
+ memcpy(new, res, sizeof(struct resource));
+
+ new->sibling = NULL;
+ if (insert_resource(&iomem_resource, new))
+ printk(KERN_WARNING
+ "Bad reserved resource %s (%08x-%08x)\n",
+ res->name, res->start, res->end);
+ }
+}
+
+static void __init
+add_physical_memory(resource_size_t start, resource_size_t end)
+{
+ struct resource *new, *next, **pprev;
+
+ for (pprev = &system_ram, next = system_ram; next;
+ pprev = &next->sibling, next = next->sibling) {
+ if (end < next->start)
+ break;
+ if (start <= next->end) {
+ printk(KERN_WARNING
+ "Warning: Physical memory map is broken\n");
+ printk(KERN_WARNING
+ "Warning: %08x-%08x overlaps %08x-%08x\n",
+ start, end, next->start, next->end);
+ return;
+ }
+ }
+
+ if (res_cache_next_free >= ARRAY_SIZE(res_cache)) {
+ printk(KERN_WARNING
+ "Warning: Failed to add physical memory %08x-%08x\n",
+ start, end);
+ return;
+ }
+
+ new = &res_cache[res_cache_next_free++];
+ new->start = start;
+ new->end = end;
+ new->name = "System RAM";
+ new->flags = IORESOURCE_MEM;
+
+ *pprev = new;
+}
+
+static int __init
+add_reserved_region(resource_size_t start, resource_size_t end,
+ const char *name)
+{
+ struct resource *new, *next, **pprev;
+
+ if (end < start)
+ return -EINVAL;
+
+ if (res_cache_next_free >= ARRAY_SIZE(res_cache))
+ return -ENOMEM;
+
+ for (pprev = &reserved, next = reserved; next;
+ pprev = &next->sibling, next = next->sibling) {
+ if (end < next->start)
+ break;
+ if (start <= next->end)
+ return -EBUSY;
+ }
+
+ new = &res_cache[res_cache_next_free++];
+ new->start = start;
+ new->end = end;
+ new->name = name;
+ new->flags = IORESOURCE_MEM;
+
+ *pprev = new;
+
+ return 0;
+}
+
+static unsigned long __init
+find_free_region(const struct resource *mem, resource_size_t size,
+ resource_size_t align)
+{
+ struct resource *res;
+ unsigned long target;
+
+ target = ALIGN(mem->start, align);
+ for (res = reserved; res; res = res->sibling) {
+ if ((target + size) <= res->start)
+ break;
+ if (target <= res->end)
+ target = ALIGN(res->end + 1, align);
+ }
+
+ if ((target + size) > (mem->end + 1))
+ return mem->end + 1;
+
+ return target;
+}
+
+static int __init
+alloc_reserved_region(resource_size_t *start, resource_size_t size,
+ resource_size_t align, const char *name)
+{
+ struct resource *mem;
+ resource_size_t target;
+ int ret;
+
+ for (mem = system_ram; mem; mem = mem->sibling) {
+ target = find_free_region(mem, size, align);
+ if (target <= mem->end) {
+ ret = add_reserved_region(target, target + size - 1,
+ name);
+ if (!ret)
+ *start = target;
+ return ret;
+ }
+ }
-#define kernel_code mem_res[0]
-#define kernel_data mem_res[1]
+ return -ENOMEM;
+}
/*
* Early framebuffer allocation. Works as follows:
* - If fbmem_size is zero, nothing will be allocated or reserved.
* - If fbmem_start is zero when setup_bootmem() is called,
- * fbmem_size bytes will be allocated from the bootmem allocator.
+ * a block of fbmem_size bytes will be reserved before bootmem
+ * initialization. It will be aligned to the largest page size
+ * that fbmem_size is a multiple of.
* - If fbmem_start is nonzero, an area of size fbmem_size will be
- * reserved at the physical address fbmem_start if necessary. If
- * the area isn't in a memory region known to the kernel, it will
- * be left alone.
+ * reserved at the physical address fbmem_start if possible. If
+ * it collides with other reserved memory, a different block of
+ * same size will be allocated, just as if fbmem_start was zero.
*
* Board-specific code may use these variables to set up platform data
* for the framebuffer driver if fbmem_size is nonzero.
*/
-static unsigned long __initdata fbmem_start;
-static unsigned long __initdata fbmem_size;
+resource_size_t __initdata fbmem_start;
+resource_size_t __initdata fbmem_size;
/*
* "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for
@@ -103,48 +243,42 @@ static unsigned long __initdata fbmem_size;
*/
static int __init early_parse_fbmem(char *p)
{
+ int ret;
+ unsigned long align;
+
fbmem_size = memparse(p, &p);
- if (*p == '@')
+ if (*p == '@') {
fbmem_start = memparse(p, &p);
- return 0;
-}
-early_param("fbmem", early_parse_fbmem);
-
-static inline void __init resource_init(void)
-{
- struct tag_mem_range *region;
-
- kernel_code.start = __pa(init_mm.start_code);
- kernel_code.end = __pa(init_mm.end_code - 1);
- kernel_data.start = __pa(init_mm.end_code);
- kernel_data.end = __pa(init_mm.brk - 1);
-
- for (region = mem_phys; region; region = region->next) {
- struct resource *res;
- unsigned long phys_start, phys_end;
-
- if (region->size == 0)
- continue;
-
- phys_start = region->addr;
- phys_end = phys_start + region->size - 1;
-
- res = alloc_bootmem_low(sizeof(*res));
- res->name = "System RAM";
- res->start = phys_start;
- res->end = phys_end;
- res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-
- request_resource (&iomem_resource, res);
+ ret = add_reserved_region(fbmem_start,
+ fbmem_start + fbmem_size - 1,
+ "Framebuffer");
+ if (ret) {
+ printk(KERN_WARNING
+ "Failed to reserve framebuffer memory\n");
+ fbmem_start = 0;
+ }
+ }
- if (kernel_code.start >= res->start &&
- kernel_code.end <= res->end)
- request_resource (res, &kernel_code);
- if (kernel_data.start >= res->start &&
- kernel_data.end <= res->end)
- request_resource (res, &kernel_data);
+ if (!fbmem_start) {
+ if ((fbmem_size & 0x000fffffUL) == 0)
+ align = 0x100000; /* 1 MiB */
+ else if ((fbmem_size & 0x0000ffffUL) == 0)
+ align = 0x10000; /* 64 KiB */
+ else
+ align = 0x1000; /* 4 KiB */
+
+ ret = alloc_reserved_region(&fbmem_start, fbmem_size,
+ align, "Framebuffer");
+ if (ret) {
+ printk(KERN_WARNING
+ "Failed to allocate framebuffer memory\n");
+ fbmem_size = 0;
+ }
}
+
+ return 0;
}
+early_param("fbmem", early_parse_fbmem);
static int __init parse_tag_core(struct tag *tag)
{
@@ -157,11 +291,9 @@ static int __init parse_tag_core(struct tag *tag)
}
__tagtable(ATAG_CORE, parse_tag_core);
-static int __init parse_tag_mem_range(struct tag *tag,
- struct tag_mem_range **root)
+static int __init parse_tag_mem(struct tag *tag)
{
- struct tag_mem_range *cur, **pprev;
- struct tag_mem_range *new;
+ unsigned long start, end;
/*
* Ignore zero-sized entries. If we're running standalone, the
@@ -171,34 +303,53 @@ static int __init parse_tag_mem_range(struct tag *tag,
if (tag->u.mem_range.size == 0)
return 0;
- /*
- * Copy the data so the bootmem init code doesn't need to care
- * about it.
- */
- if (mem_range_next_free >= ARRAY_SIZE(mem_range_cache))
- panic("Physical memory map too complex!\n");
+ start = tag->u.mem_range.addr;
+ end = tag->u.mem_range.addr + tag->u.mem_range.size - 1;
+
+ add_physical_memory(start, end);
+ return 0;
+}
+__tagtable(ATAG_MEM, parse_tag_mem);
+
+static int __init parse_tag_rdimg(struct tag *tag)
+{
+#ifdef CONFIG_INITRD
+ struct tag_mem_range *mem = &tag->u.mem_range;
+ int ret;
- new = &mem_range_cache[mem_range_next_free++];
- *new = tag->u.mem_range;
+ if (initrd_start) {
+ printk(KERN_WARNING
+ "Warning: Only the first initrd image will be used\n");
+ return 0;
+ }
- pprev = root;
- cur = *root;
- while (cur) {
- pprev = &cur->next;
- cur = cur->next;
+ ret = add_reserved_region(mem->start, mem->start + mem->size - 1,
+ "initrd");
+ if (ret) {
+ printk(KERN_WARNING
+ "Warning: Failed to reserve initrd memory\n");
+ return ret;
}
- *pprev = new;
- new->next = NULL;
+ initrd_start = (unsigned long)__va(mem->addr);
+ initrd_end = initrd_start + mem->size;
+#else
+ printk(KERN_WARNING "RAM disk image present, but "
+ "no initrd support in kernel, ignoring\n");
+#endif
return 0;
}
+__tagtable(ATAG_RDIMG, parse_tag_rdimg);
-static int __init parse_tag_mem(struct tag *tag)
+static int __init parse_tag_rsvd_mem(struct tag *tag)
{
- return parse_tag_mem_range(tag, &mem_phys);
+ struct tag_mem_range *mem = &tag->u.mem_range;
+
+ return add_reserved_region(mem->addr, mem->addr + mem->size - 1,
+ "Reserved");
}
-__tagtable(ATAG_MEM, parse_tag_mem);
+__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
static int __init parse_tag_cmdline(struct tag *tag)
{
@@ -207,12 +358,6 @@ static int __init parse_tag_cmdline(struct tag *tag)
}
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
-static int __init parse_tag_rdimg(struct tag *tag)
-{
- return parse_tag_mem_range(tag, &mem_ramdisk);
-}
-__tagtable(ATAG_RDIMG, parse_tag_rdimg);
-
static int __init parse_tag_clock(struct tag *tag)
{
/*
@@ -223,12 +368,6 @@ static int __init parse_tag_clock(struct tag *tag)
}
__tagtable(ATAG_CLOCK, parse_tag_clock);
-static int __init parse_tag_rsvd_mem(struct tag *tag)
-{
- return parse_tag_mem_range(tag, &mem_reserved);
-}
-__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
-
/*
* Scan the tag table for this tag, and call its parse function. The
* tag table is built by the linker from all the __tagtable
@@ -260,10 +399,137 @@ static void __init parse_tags(struct tag *t)
t->hdr.tag);
}
+/*
+ * Find a free memory region large enough for storing the
+ * bootmem bitmap.
+ */
+static unsigned long __init
+find_bootmap_pfn(const struct resource *mem)
+{
+ unsigned long bootmap_pages, bootmap_len;
+ unsigned long node_pages = PFN_UP(mem->end - mem->start + 1);
+ unsigned long bootmap_start;
+
+ bootmap_pages = bootmem_bootmap_pages(node_pages);
+ bootmap_len = bootmap_pages << PAGE_SHIFT;
+
+ /*
+ * Find a large enough region without reserved pages for
+ * storing the bootmem bitmap. We can take advantage of the
+ * fact that all lists have been sorted.
+ *
+ * We have to check that we don't collide with any reserved
+ * regions, which includes the kernel image and any RAMDISK
+ * images.
+ */
+ bootmap_start = find_free_region(mem, bootmap_len, PAGE_SIZE);
+
+ return bootmap_start >> PAGE_SHIFT;
+}
+
+#define MAX_LOWMEM HIGHMEM_START
+#define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM)
+
+static void __init setup_bootmem(void)
+{
+ unsigned bootmap_size;
+ unsigned long first_pfn, bootmap_pfn, pages;
+ unsigned long max_pfn, max_low_pfn;
+ unsigned node = 0;
+ struct resource *res;
+
+ printk(KERN_INFO "Physical memory:\n");
+ for (res = system_ram; res; res = res->sibling)
+ printk(" %08x-%08x\n", res->start, res->end);
+ printk(KERN_INFO "Reserved memory:\n");
+ for (res = reserved; res; res = res->sibling)
+ printk(" %08x-%08x: %s\n",
+ res->start, res->end, res->name);
+
+ nodes_clear(node_online_map);
+
+ if (system_ram->sibling)
+ printk(KERN_WARNING "Only using first memory bank\n");
+
+ for (res = system_ram; res; res = NULL) {
+ first_pfn = PFN_UP(res->start);
+ max_low_pfn = max_pfn = PFN_DOWN(res->end + 1);
+ bootmap_pfn = find_bootmap_pfn(res);
+ if (bootmap_pfn > max_pfn)
+ panic("No space for bootmem bitmap!\n");
+
+ if (max_low_pfn > MAX_LOWMEM_PFN) {
+ max_low_pfn = MAX_LOWMEM_PFN;
+#ifndef CONFIG_HIGHMEM
+ /*
+ * Lowmem is memory that can be addressed
+ * directly through P1/P2
+ */
+ printk(KERN_WARNING
+ "Node %u: Only %ld MiB of memory will be used.\n",
+ node, MAX_LOWMEM >> 20);
+ printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
+#else
+#error HIGHMEM is not supported by AVR32 yet
+#endif
+ }
+
+ /* Initialize the boot-time allocator with low memory only. */
+ bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn,
+ first_pfn, max_low_pfn);
+
+ /*
+ * Register fully available RAM pages with the bootmem
+ * allocator.
+ */
+ pages = max_low_pfn - first_pfn;
+ free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn),
+ PFN_PHYS(pages));
+
+ /* Reserve space for the bootmem bitmap... */
+ reserve_bootmem_node(NODE_DATA(node),
+ PFN_PHYS(bootmap_pfn),
+ bootmap_size);
+
+ /* ...and any other reserved regions. */
+ for (res = reserved; res; res = res->sibling) {
+ if (res->start > PFN_PHYS(max_pfn))