diff options
Diffstat (limited to 'arch/hexagon/kernel')
30 files changed, 4591 insertions, 0 deletions
diff --git a/arch/hexagon/kernel/Makefile b/arch/hexagon/kernel/Makefile new file mode 100644 index 00000000000..3689f3754d0 --- /dev/null +++ b/arch/hexagon/kernel/Makefile @@ -0,0 +1,18 @@ +extra-y := head.o vmlinux.lds init_task.o + +obj-$(CONFIG_SMP) += smp.o topology.o + +obj-y += setup.o irq_cpu.o traps.o syscalltab.o signal.o time.o +obj-y += process.o syscall.o trampoline.o reset.o ptrace.o +obj-y += vdso.o + +obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_MODULES) += module.o hexagon_ksyms.o + +# Modules required to work with the Hexagon Virtual Machine +obj-y += vm_entry.o vm_events.o vm_switch.o vm_ops.o vm_init_segtable.o +obj-y += vm_vectors.o + +obj-$(CONFIG_HAS_DMA) += dma.o + +obj-$(CONFIG_STACKTRACE) += stacktrace.o diff --git a/arch/hexagon/kernel/asm-offsets.c b/arch/hexagon/kernel/asm-offsets.c new file mode 100644 index 00000000000..89ffa514611 --- /dev/null +++ b/arch/hexagon/kernel/asm-offsets.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 1996 David S. Miller + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/compat.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/kbuild.h> +#include <asm/ptrace.h> +#include <asm/processor.h> + +/* This file is used to produce asm/linkerscript constants from header + files typically used in c. Specifically, it generates asm-offsets.h */ + +int main(void) +{ + COMMENT("This is a comment."); + /* might get these from somewhere else. */ + DEFINE(_PAGE_SIZE, PAGE_SIZE); + DEFINE(_PAGE_SHIFT, PAGE_SHIFT); + BLANK(); + + COMMENT("Hexagon pt_regs definitions"); + OFFSET(_PT_SYSCALL_NR, pt_regs, syscall_nr); + OFFSET(_PT_UGPGP, pt_regs, ugpgp); + OFFSET(_PT_R3130, pt_regs, r3130); + OFFSET(_PT_R2928, pt_regs, r2928); + OFFSET(_PT_R2726, pt_regs, r2726); + OFFSET(_PT_R2524, pt_regs, r2524); + OFFSET(_PT_R2322, pt_regs, r2322); + OFFSET(_PT_R2120, pt_regs, r2120); + OFFSET(_PT_R1918, pt_regs, r1918); + OFFSET(_PT_R1716, pt_regs, r1716); + OFFSET(_PT_R1514, pt_regs, r1514); + OFFSET(_PT_R1312, pt_regs, r1312); + OFFSET(_PT_R1110, pt_regs, r1110); + OFFSET(_PT_R0908, pt_regs, r0908); + OFFSET(_PT_R0706, pt_regs, r0706); + OFFSET(_PT_R0504, pt_regs, r0504); + OFFSET(_PT_R0302, pt_regs, r0302); + OFFSET(_PT_R0100, pt_regs, r0100); + OFFSET(_PT_LC0SA0, pt_regs, lc0sa0); + OFFSET(_PT_LC1SA1, pt_regs, lc1sa1); + OFFSET(_PT_M1M0, pt_regs, m1m0); + OFFSET(_PT_PREDSUSR, pt_regs, predsusr); + OFFSET(_PT_EVREC, pt_regs, hvmer); + OFFSET(_PT_ER_VMEL, pt_regs, hvmer.vmel); + OFFSET(_PT_ER_VMEST, pt_regs, hvmer.vmest); + OFFSET(_PT_ER_VMPSP, pt_regs, hvmer.vmpsp); + OFFSET(_PT_ER_VMBADVA, pt_regs, hvmer.vmbadva); + DEFINE(_PT_REGS_SIZE, sizeof(struct pt_regs)); + BLANK(); + + COMMENT("Hexagon thread_info definitions"); + OFFSET(_THREAD_INFO_FLAGS, thread_info, flags); + OFFSET(_THREAD_INFO_PT_REGS, thread_info, regs); + OFFSET(_THREAD_INFO_SP, thread_info, sp); + DEFINE(_THREAD_SIZE, THREAD_SIZE); + BLANK(); + + COMMENT("Hexagon hexagon_switch_stack definitions"); + OFFSET(_SWITCH_R1716, hexagon_switch_stack, r1716); + OFFSET(_SWITCH_R1918, hexagon_switch_stack, r1918); + OFFSET(_SWITCH_R2120, hexagon_switch_stack, r2120); + OFFSET(_SWITCH_R2322, hexagon_switch_stack, r2322); + + OFFSET(_SWITCH_R2524, hexagon_switch_stack, r2524); + OFFSET(_SWITCH_R2726, hexagon_switch_stack, r2726); + OFFSET(_SWITCH_FP, hexagon_switch_stack, fp); + OFFSET(_SWITCH_LR, hexagon_switch_stack, lr); + DEFINE(_SWITCH_STACK_SIZE, sizeof(struct hexagon_switch_stack)); + BLANK(); + + COMMENT("Hexagon task_struct definitions"); + OFFSET(_TASK_THREAD_INFO, task_struct, stack); + OFFSET(_TASK_STRUCT_THREAD, task_struct, thread); + + COMMENT("Hexagon thread_struct definitions"); + OFFSET(_THREAD_STRUCT_SWITCH_SP, thread_struct, switch_sp); + + return 0; +} diff --git a/arch/hexagon/kernel/dma.c b/arch/hexagon/kernel/dma.c new file mode 100644 index 00000000000..e711ace62fd --- /dev/null +++ b/arch/hexagon/kernel/dma.c @@ -0,0 +1,220 @@ +/* + * DMA implementation for Hexagon + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/dma-mapping.h> +#include <linux/bootmem.h> +#include <linux/genalloc.h> +#include <asm/dma-mapping.h> + +struct dma_map_ops *dma_ops; +EXPORT_SYMBOL(dma_ops); + +int bad_dma_address; /* globals are automatically initialized to zero */ + +int dma_supported(struct device *dev, u64 mask) +{ + if (mask == DMA_BIT_MASK(32)) + return 1; + else + return 0; +} +EXPORT_SYMBOL(dma_supported); + +int dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} +EXPORT_SYMBOL(dma_set_mask); + +static struct gen_pool *coherent_pool; + + +/* Allocates from a pool of uncached memory that was reserved at boot time */ + +void *hexagon_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_addr, gfp_t flag) +{ + void *ret; + + if (coherent_pool == NULL) { + coherent_pool = gen_pool_create(PAGE_SHIFT, -1); + + if (coherent_pool == NULL) + panic("Can't create %s() memory pool!", __func__); + else + gen_pool_add(coherent_pool, + (PAGE_OFFSET + (max_low_pfn << PAGE_SHIFT)), + hexagon_coherent_pool_size, -1); + } + + ret = (void *) gen_pool_alloc(coherent_pool, size); + + if (ret) { + memset(ret, 0, size); + *dma_addr = (dma_addr_t) (ret - PAGE_OFFSET); + } else + *dma_addr = ~0; + + return ret; +} + +static void hexagon_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_addr) +{ + gen_pool_free(coherent_pool, (unsigned long) vaddr, size); +} + +static int check_addr(const char *name, struct device *hwdev, + dma_addr_t bus, size_t size) +{ + if (hwdev && hwdev->dma_mask && !dma_capable(hwdev, bus, size)) { + if (*hwdev->dma_mask >= DMA_BIT_MASK(32)) + printk(KERN_ERR + "%s: overflow %Lx+%zu of device mask %Lx\n", + name, (long long)bus, size, + (long long)*hwdev->dma_mask); + return 0; + } + return 1; +} + +static int hexagon_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct scatterlist *s; + int i; + + WARN_ON(nents == 0 || sg[0].length == 0); + + for_each_sg(sg, s, nents, i) { + s->dma_address = sg_phys(s); + if (!check_addr("map_sg", hwdev, s->dma_address, s->length)) + return 0; + + s->dma_length = s->length; + + flush_dcache_range(PAGE_OFFSET + s->dma_address, + PAGE_OFFSET + s->dma_address + s->length); + } + + return nents; +} + +/* + * address is virtual + */ +static inline void dma_sync(void *addr, size_t size, + enum dma_data_direction dir) +{ + switch (dir) { + case DMA_TO_DEVICE: + hexagon_clean_dcache_range((unsigned long) addr, + (unsigned long) addr + size); + break; + case DMA_FROM_DEVICE: + hexagon_inv_dcache_range((unsigned long) addr, + (unsigned long) addr + size); + break; + case DMA_BIDIRECTIONAL: + flush_dcache_range((unsigned long) addr, + (unsigned long) addr + size); + break; + default: + BUG(); + } +} + +static inline void *dma_addr_to_virt(dma_addr_t dma_addr) +{ + return phys_to_virt((unsigned long) dma_addr); +} + +/** + * hexagon_map_page() - maps an address for device DMA + * @dev: pointer to DMA device + * @page: pointer to page struct of DMA memory + * @offset: offset within page + * @size: size of memory to map + * @dir: transfer direction + * @attrs: pointer to DMA attrs (not used) + * + * Called to map a memory address to a DMA address prior + * to accesses to/from device. + * + * We don't particularly have many hoops to jump through + * so far. Straight translation between phys and virtual. + * + * DMA is not cache coherent so sync is necessary; this + * seems to be a convenient place to do it. + * + */ +static dma_addr_t hexagon_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + dma_addr_t bus = page_to_phys(page) + offset; + WARN_ON(size == 0); + + if (!check_addr("map_single", dev, bus, size)) + return bad_dma_address; + + dma_sync(dma_addr_to_virt(bus), size, dir); + + return bus; +} + +static void hexagon_sync_single_for_cpu(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) +{ + dma_sync(dma_addr_to_virt(dma_handle), size, dir); +} + +static void hexagon_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) +{ + dma_sync(dma_addr_to_virt(dma_handle), size, dir); +} + +struct dma_map_ops hexagon_dma_ops = { + .alloc_coherent = hexagon_dma_alloc_coherent, + .free_coherent = hexagon_free_coherent, + .map_sg = hexagon_map_sg, + .map_page = hexagon_map_page, + .sync_single_for_cpu = hexagon_sync_single_for_cpu, + .sync_single_for_device = hexagon_sync_single_for_device, + .is_phys = 1, +}; + +void __init hexagon_dma_init(void) +{ + if (dma_ops) + return; + + dma_ops = &hexagon_dma_ops; +} diff --git a/arch/hexagon/kernel/head.S b/arch/hexagon/kernel/head.S new file mode 100644 index 00000000000..8e6b819125a --- /dev/null +++ b/arch/hexagon/kernel/head.S @@ -0,0 +1,162 @@ +/* + * Early kernel startup code for Hexagon + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/asm-offsets.h> +#include <asm/mem-layout.h> +#include <asm/vm_mmu.h> +#include <asm/page.h> + + __INIT +ENTRY(stext) + /* + * VMM will already have set up true vector page, MMU, etc. + * To set up initial kernel identity map, we have to pass + * the VMM a pointer to some canonical page tables. In + * this implementation, we're assuming that we've got + * them precompiled. Generate value in R24, as we'll need + * it again shortly. + */ + r24.L = #LO(swapper_pg_dir) + r24.H = #HI(swapper_pg_dir) + + /* + * Symbol is kernel segment address, but we need + * the logical/physical address. + */ + r24 = asl(r24, #2) + r24 = lsr(r24, #2) + + r0 = r24 + + /* + * Initialize a 16MB PTE to make the virtual and physical + * addresses where the kernel was loaded be identical. + */ +#define PTE_BITS ( __HVM_PTE_R | __HVM_PTE_W | __HVM_PTE_X \ + | __HEXAGON_C_WB_L2 << 6 \ + | __HVM_PDE_S_4MB) + + r1 = pc + r2.H = #0xffc0 + r2.L = #0x0000 + r1 = and(r1,r2) /* round PC to 4MB boundary */ + r2 = lsr(r1, #22) /* 4MB page number */ + r2 = asl(r2, #2) /* times sizeof(PTE) (4bytes) */ + r0 = add(r0,r2) /* r0 = address of correct PTE */ + r2 = #PTE_BITS + r1 = add(r1,r2) /* r1 = 4MB PTE for the first entry */ + r2.h = #0x0040 + r2.l = #0x0000 /* 4MB */ + memw(r0 ++ #4) = r1 + r1 = add(r1, r2) + memw(r0 ++ #4) = r1 + + r0 = r24 + + /* + * The subroutine wrapper around the virtual instruction touches + * no memory, so we should be able to use it even here. + */ + call __vmnewmap; + + /* Jump into virtual address range. */ + + r31.h = #hi(__head_s_vaddr_target) + r31.l = #lo(__head_s_vaddr_target) + jumpr r31 + + /* Insert trippy space effects. */ + +__head_s_vaddr_target: + /* + * Tear down VA=PA translation now that we are running + * in the desgnated kernel segments. + */ + r0 = #__HVM_PDE_S_INVALID + r1 = r24 + loop0(1f,#0x100) +1: + { + memw(R1 ++ #4) = R0 + }:endloop0 + + r0 = r24 + call __vmnewmap + + /* Go ahead and install the trap0 return so angel calls work */ + r0.h = #hi(_K_provisional_vec) + r0.l = #lo(_K_provisional_vec) + call __vmsetvec + + /* + * OK, at this point we should start to be much more careful, + * we're going to enter C code and start touching memory + * in all sorts of places. + * This means: + * SGP needs to be OK + * Need to lock shared resources + * A bunch of other things that will cause + * all kinds of painful bugs + */ + + /* + * Stack pointer should be pointed at the init task's + * thread stack, which should have been declared in arch/init_task.c. + * So uhhhhh... + * It's accessible via the init_thread_union, which is a union + * of a thread_info struct and a stack; of course, the top + * of the stack is not for you. The end of the stack + * is simply init_thread_union + THREAD_SIZE. + */ + + {r29.H = #HI(init_thread_union); r0.H = #HI(_THREAD_SIZE); } + {r29.L = #LO(init_thread_union); r0.L = #LO(_THREAD_SIZE); } + + /* initialize the register used to point to current_thread_info */ + /* Fixme: THREADINFO_REG can't be R2 because of that memset thing. */ + {r29 = add(r29,r0); THREADINFO_REG = r29; } + + /* Hack: zero bss; */ + { r0.L = #LO(__bss_start); r1 = #0; r2.l = #LO(__bss_stop); } + { r0.H = #HI(__bss_start); r2.h = #HI(__bss_stop); } + + r2 = sub(r2,r0); + call memset; + + /* Time to make the doughnuts. */ + call start_kernel + + /* + * Should not reach here. + */ +1: + jump 1b + +.p2align PAGE_SHIFT +ENTRY(external_cmdline_buffer) + .fill _PAGE_SIZE,1,0 + +.data +.p2align PAGE_SHIFT +ENTRY(empty_zero_page) + .fill _PAGE_SIZE,1,0 diff --git a/arch/hexagon/kernel/hexagon_ksyms.c b/arch/hexagon/kernel/hexagon_ksyms.c new file mode 100644 index 00000000000..7f189247180 --- /dev/null +++ b/arch/hexagon/kernel/hexagon_ksyms.c @@ -0,0 +1,40 @@ +/* + * Export of symbols defined in assembly files and/or libgcc. + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <asm/hexagon_vm.h> +#include <asm/uaccess.h> + +EXPORT_SYMBOL(__copy_from_user_hexagon); +EXPORT_SYMBOL(__copy_to_user_hexagon); +EXPORT_SYMBOL(__vmgetie); +EXPORT_SYMBOL(__vmsetie); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); + +#define DECLARE_EXPORT(name) \ + extern void name(void); EXPORT_SYMBOL(name) + +/* Symbols found in libgcc that assorted kernel modules need */ +DECLARE_EXPORT(__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes); + +DECLARE_EXPORT(__hexagon_divsi3); +DECLARE_EXPORT(__hexagon_modsi3); +DECLARE_EXPORT(__hexagon_udivsi3); +DECLARE_EXPORT(__hexagon_umodsi3); diff --git a/arch/hexagon/kernel/init_task.c b/arch/hexagon/kernel/init_task.c new file mode 100644 index 00000000000..73283d3edf0 --- /dev/null +++ b/arch/hexagon/kernel/init_task.c @@ -0,0 +1,54 @@ +/* + * Init task definition + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> +#include <asm/thread_info.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"), + __aligned__(THREAD_SIZE))) = { + INIT_THREAD_INFO(init_task) + }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); diff --git a/arch/hexagon/kernel/irq_cpu.c b/arch/hexagon/kernel/irq_cpu.c new file mode 100644 index 00000000000..d4416a1a431 --- /dev/null +++ b/arch/hexagon/kernel/irq_cpu.c @@ -0,0 +1,90 @@ +/* + * First-level interrupt controller model for Hexagon. + * + * Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/interrupt.h> +#include <asm/irq.h> +#include <asm/hexagon_vm.h> + +static void mask_irq(struct irq_data *data) +{ + __vmintop_locdis((long) data->irq); +} + +static void mask_irq_num(unsigned int irq) +{ + __vmintop_locdis((long) irq); +} + +static void unmask_irq(struct irq_data *data) +{ + __vmintop_locen((long) data->irq); +} + +/* This is actually all we need for handle_fasteoi_irq */ +static void eoi_irq(struct irq_data *data) +{ + __vmintop_globen((long) data->irq); +} + +/* Power mamangement wake call. We don't need this, however, + * if this is absent, then an -ENXIO error is returned to the + * msm_serial driver, and it fails to correctly initialize. + * This is a bug in the msm_serial driver, but, for now, we + * work around it here, by providing this bogus handler. + * XXX FIXME!!! remove this when msm_serial is fixed. + */ +static int set_wake(struct irq_data *data, unsigned int on) +{ + return 0; +} + +static struct irq_chip hexagon_irq_chip = { + .name = "HEXAGON", + .irq_mask = mask_irq, + .irq_unmask = unmask_irq, + .irq_set_wake = set_wake, + .irq_eoi = eoi_irq +}; + +/** + * The hexagon core comes with a first-level interrupt controller + * with 32 total possible interrupts. When the core is embedded + * into different systems/platforms, it is typically wrapped by + * macro cells that provide one or more second-level interrupt + * controllers that are cascaded into one or more of the first-level + * interrupts handled here. The precise wiring of these other + * irqs varies from platform to platform, and are set up & configured + * in the platform-specific files. + * + * The first-level interrupt controller is wrapped by the VM, which + * virtualizes the interrupt controller for us. It provides a very + * simple, fast & efficient API, and so the fasteoi handler is + * appropriate for this case. + */ +void __init init_IRQ(void) +{ + int irq; + + for (irq = 0; irq < HEXAGON_CPUINTS; irq++) { + mask_irq_num(irq); + irq_set_chip_and_handler(irq, &hexagon_irq_chip, + handle_fasteoi_irq); + } +} diff --git a/arch/hexagon/kernel/kgdb.c b/arch/hexagon/kernel/kgdb.c new file mode 100644 index 00000000000..fe4aa1bcca5 --- /dev/null +++ b/arch/hexagon/kernel/kgdb.c @@ -0,0 +1,254 @@ +/* + * arch/hexagon/kernel/kgdb.c - Hexagon KGDB Support + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/kdebug.h> +#include <linux/kgdb.h> + +/* All registers are 4 bytes, for now */ +#define GDB_SIZEOF_REG 4 + +/* The register names are used during printing of the regs; + * Keep these at three letters to pretty-print. */ +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { " r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, r00)}, + { " r1", GDB_SIZEOF_REG, offsetof(struct pt_regs, r01)}, + { " r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r02)}, + { " r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r03)}, + { " r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r04)}, + { " r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r05)}, + { " r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r06)}, + { " r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r07)}, + { " r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r08)}, + { " r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r09)}, + { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10)}, + { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11)}, + { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12)}, + { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13)}, + { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14)}, + { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15)}, + { "r16", GDB_SIZEOF_REG, offsetof(struct pt_regs, r16)}, + { "r17", GDB_SIZEOF_REG, offsetof(struct pt_regs, r17)}, + { "r18", GDB_SIZEOF_REG, offsetof(struct pt_regs, r18)}, + { "r19", GDB_SIZEOF_REG, offsetof(struct pt_regs, r19)}, + { "r20", GDB_SIZEOF_REG, offsetof(struct pt_regs, r20)}, + { "r21", GDB_SIZEOF_REG, offsetof(struct pt_regs, r21)}, + { "r22", GDB_SIZEOF_REG, offsetof(struct pt_regs, r22)}, + { "r23", GDB_SIZEOF_REG, offsetof(struct pt_regs, r23)}, + { "r24", GDB_SIZEOF_REG, offsetof(struct pt_regs, r24)}, + { "r25", GDB_SIZEOF_REG, offsetof(struct pt_regs, r25)}, + { "r26", GDB_SIZEOF_REG, offsetof(struct pt_regs, r26)}, + { "r27", GDB_SIZEOF_REG, offsetof(struct pt_regs, r27)}, + { "r28", GDB_SIZEOF_REG, offsetof(struct pt_regs, r28)}, + { "r29", GDB_SIZEOF_REG, offsetof(struct pt_regs, r29)}, + { "r30", GDB_SIZEOF_REG, offsetof(struct pt_regs, r30)}, + { "r31", GDB_SIZEOF_REG, offsetof(struct pt_regs, r31)}, + + { "usr", GDB_SIZEOF_REG, offsetof(struct pt_regs, usr)}, + { "preds", GDB_SIZEOF_REG, offsetof(struct pt_regs, preds)}, + { " m0", GDB_SIZEOF_REG, offsetof(struct pt_regs, m0)}, + { " m1", GDB_SIZEOF_REG, offsetof(struct pt_regs, m1)}, + { "sa0", GDB_SIZEOF_REG, offsetof(struct pt_regs, sa0)}, + { "sa1", GDB_SIZEOF_REG, offsetof(struct pt_regs, sa1)}, + { "lc0", GDB_SIZEOF_REG, offsetof(struct pt_regs, lc0)}, + { "lc1", GDB_SIZEOF_REG, offsetof(struct pt_regs, lc1)}, + { " gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp)}, + { "ugp", GDB_SIZEOF_REG, offsetof(struct pt_regs, ugp)}, + { "psp", GDB_SIZEOF_REG, offsetof(struct pt_regs, hvmer.vmpsp)}, + { "elr", GDB_SIZEOF_REG, offsetof(struct pt_regs, hvmer.vmel)}, + { "est", GDB_SIZEOF_REG, offsetof(struct pt_regs, hvmer.vmest)}, + { "badva", GDB_SIZEOF_REG, offsetof(struct pt_regs, hvmer.vmbadva)}, + { "restart_r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, restart_r0)}, + { "syscall_nr", GDB_SIZEOF_REG, offsetof(struct pt_regs, syscall_nr)}, +}; + +struct kgdb_arch arch_kgdb_ops = { + /* trap0(#0xDB) 0x0cdb0054 */ + .gdb_bpt_instr = {0x54, 0x00, 0xdb, 0x0c}, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + *((unsigned long *) mem) = *((unsigned long *) ((void *)regs + + dbg_reg_def[regno].offset)); + + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + *((unsigned long *) ((void *)regs + dbg_reg_def[regno].offset)) = + *((unsigned long *) mem); + + return 0; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + instruction_pointer(regs) = pc; +} + +#ifdef CONFIG_SMP + +/** + * kgdb_roundup_cpus - Get other CPUs into a holding pattern + * @flags: Current IRQ state + * + * On SMP systems, we need to get the attention of the other CPUs + * and get them be in a known state. This should do what is needed + * to get the other CPUs to call kgdb_wait(). Note that on some arches, + * the NMI approach is not used for rounding up all the CPUs. For example, + * in case of MIPS, smp_call_function() is used to roundup CPUs. In + * this case, we have to make sure that interrupts are enabled before + * calling smp_call_function(). The argument to this function is + * the flags that will be used when restoring the interrupts. There is + * local_irq_save() call before kgdb_roundup_cpus(). + * + * On non-SMP systems, this is not called. + */ + +static void hexagon_kgdb_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); +} + +void kgdb_roundup_cpus(unsigned long flags) |