diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index a4787197d8fe..7589051e79e0 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -7,6 +7,7 @@ config OPENRISC def_bool y select OF select OF_EARLY_FLATTREE + select IRQ_DOMAIN select HAVE_MEMBLOCK select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_ARCH_TRACEHOOK diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild index dcea5a0308ae..c936483bc8e2 100644 --- a/arch/openrisc/include/asm/Kbuild +++ b/arch/openrisc/include/asm/Kbuild @@ -1,6 +1,7 @@ include include/asm-generic/Kbuild.asm -header-y += spr_defs.h +header-y += elf.h +header-y += ucontext.h generic-y += atomic.h generic-y += auxvec.h diff --git a/arch/openrisc/include/asm/dma-mapping.h b/arch/openrisc/include/asm/dma-mapping.h index b206ba4608b2..fab8628e1b6e 100644 --- a/arch/openrisc/include/asm/dma-mapping.h +++ b/arch/openrisc/include/asm/dma-mapping.h @@ -20,150 +20,71 @@ /* * See Documentation/DMA-API-HOWTO.txt and * Documentation/DMA-API.txt for documentation. - * - * This file is written with the intention of eventually moving over - * to largely using asm-generic/dma-mapping-common.h in its place. */ #include #include #include +#include #define DMA_ERROR_CODE (~(dma_addr_t)0x0) +extern struct dma_map_ops or1k_dma_map_ops; -#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) -#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) - -void *or1k_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag); -void or1k_dma_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle); -dma_addr_t or1k_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - struct dma_attrs *attrs); -void or1k_unmap_page(struct device *dev, dma_addr_t dma_handle, - size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs); -int or1k_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - struct dma_attrs *attrs); -void or1k_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - struct dma_attrs *attrs); -void or1k_sync_single_for_cpu(struct device *dev, - dma_addr_t dma_handle, size_t size, - enum dma_data_direction dir); -void or1k_sync_single_for_device(struct device *dev, - dma_addr_t dma_handle, size_t size, - enum dma_data_direction dir); - -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) +static inline struct dma_map_ops *get_dma_ops(struct device *dev) { + return &or1k_dma_map_ops; +} + +#include + +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) +{ + struct dma_map_ops *ops = get_dma_ops(dev); void *memory; - memory = or1k_dma_alloc_coherent(dev, size, dma_handle, flag); + memory = ops->alloc(dev, size, dma_handle, gfp, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); + return memory; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) { + struct dma_map_ops *ops = get_dma_ops(dev); + debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); - or1k_dma_free_coherent(dev, size, cpu_addr, dma_handle); + + ops->free(dev, size, cpu_addr, dma_handle, attrs); } -static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, - size_t size, - enum dma_data_direction dir) +static inline void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) { - dma_addr_t addr; + struct dma_attrs attrs; - kmemcheck_mark_initialized(ptr, size); - BUG_ON(!valid_dma_direction(dir)); - addr = or1k_map_page(dev, virt_to_page(ptr), - (unsigned long)ptr & ~PAGE_MASK, size, - dir, NULL); - debug_dma_map_page(dev, virt_to_page(ptr), - (unsigned long)ptr & ~PAGE_MASK, size, - dir, addr, true); - return addr; + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + + return dma_alloc_attrs(dev, size, dma_handle, gfp, &attrs); } -static inline void dma_unmap_single(struct device *dev, dma_addr_t addr, - size_t size, - enum dma_data_direction dir) +static inline void dma_free_noncoherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle) { - BUG_ON(!valid_dma_direction(dir)); - or1k_unmap_page(dev, addr, size, dir, NULL); - debug_dma_unmap_page(dev, addr, size, dir, true); -} + struct dma_attrs attrs; -static inline int dma_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - int i, ents; - struct scatterlist *s; + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); - for_each_sg(sg, s, nents, i) - kmemcheck_mark_initialized(sg_virt(s), s->length); - BUG_ON(!valid_dma_direction(dir)); - ents = or1k_map_sg(dev, sg, nents, dir, NULL); - debug_dma_map_sg(dev, sg, nents, ents, dir); - - return ents; -} - -static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - BUG_ON(!valid_dma_direction(dir)); - debug_dma_unmap_sg(dev, sg, nents, dir); - or1k_unmap_sg(dev, sg, nents, dir, NULL); -} - -static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, - size_t offset, size_t size, - enum dma_data_direction dir) -{ - dma_addr_t addr; - - kmemcheck_mark_initialized(page_address(page) + offset, size); - BUG_ON(!valid_dma_direction(dir)); - addr = or1k_map_page(dev, page, offset, size, dir, NULL); - debug_dma_map_page(dev, page, offset, size, dir, addr, false); - - return addr; -} - -static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - BUG_ON(!valid_dma_direction(dir)); - or1k_unmap_page(dev, addr, size, dir, NULL); - debug_dma_unmap_page(dev, addr, size, dir, true); -} - -static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, - size_t size, - enum dma_data_direction dir) -{ - BUG_ON(!valid_dma_direction(dir)); - or1k_sync_single_for_cpu(dev, addr, size, dir); - debug_dma_sync_single_for_cpu(dev, addr, size, dir); -} - -static inline void dma_sync_single_for_device(struct device *dev, - dma_addr_t addr, size_t size, - enum dma_data_direction dir) -{ - BUG_ON(!valid_dma_direction(dir)); - or1k_sync_single_for_device(dev, addr, size, dir); - debug_dma_sync_single_for_device(dev, addr, size, dir); + dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs); } static inline int dma_supported(struct device *dev, u64 dma_mask) diff --git a/arch/openrisc/include/asm/elf.h b/arch/openrisc/include/asm/elf.h index 2ce603bbfdd3..a8fe2c513070 100644 --- a/arch/openrisc/include/asm/elf.h +++ b/arch/openrisc/include/asm/elf.h @@ -19,12 +19,18 @@ #ifndef __ASM_OPENRISC_ELF_H #define __ASM_OPENRISC_ELF_H +/* + * This files is partially exported to userspace. This allows us to keep + * the ELF bits in one place which should assist in keeping the kernel and + * userspace in sync. + */ + /* * ELF register definitions.. */ -#include -#include +/* for struct user_regs_struct definition */ +#include /* The OR1K relocation types... not all relevant for module loader */ #define R_OR32_NONE 0 @@ -62,6 +68,8 @@ typedef unsigned long elf_fpregset_t; #ifdef __KERNEL__ +#include + /* * This is used to ensure we don't load something for the wrong architecture. */ diff --git a/arch/openrisc/include/asm/ptrace.h b/arch/openrisc/include/asm/ptrace.h index 4651a737591d..8555c0c3d4d7 100644 --- a/arch/openrisc/include/asm/ptrace.h +++ b/arch/openrisc/include/asm/ptrace.h @@ -19,8 +19,6 @@ #ifndef __ASM_OPENRISC_PTRACE_H #define __ASM_OPENRISC_PTRACE_H -#include - #ifndef __ASSEMBLY__ /* * This is the layout of the regset returned by the GETREGSET ptrace call @@ -30,13 +28,13 @@ struct user_regs_struct { unsigned long gpr[32]; unsigned long pc; unsigned long sr; - unsigned long pad1; - unsigned long pad2; }; #endif #ifdef __KERNEL__ +#include + /* * Make kernel PTrace/register structures opaque to userspace... userspace can * access thread state via the regset mechanism. This allows us a bit of diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c index f1c8ee2895d0..0b77ddb1ee07 100644 --- a/arch/openrisc/kernel/dma.c +++ b/arch/openrisc/kernel/dma.c @@ -21,13 +21,16 @@ #include #include +#include +#include #include #include #include -static int page_set_nocache(pte_t *pte, unsigned long addr, - unsigned long next, struct mm_walk *walk) +static int +page_set_nocache(pte_t *pte, unsigned long addr, + unsigned long next, struct mm_walk *walk) { unsigned long cl; @@ -46,8 +49,9 @@ static int page_set_nocache(pte_t *pte, unsigned long addr, return 0; } -static int page_clear_nocache(pte_t *pte, unsigned long addr, - unsigned long next, struct mm_walk *walk) +static int +page_clear_nocache(pte_t *pte, unsigned long addr, + unsigned long next, struct mm_walk *walk) { pte_val(*pte) &= ~_PAGE_CI; @@ -67,9 +71,19 @@ static int page_clear_nocache(pte_t *pte, unsigned long addr, * cache-inhibit bit on those pages, and makes sure that the pages are * flushed out of the cache before they are used. * + * If the NON_CONSISTENT attribute is set, then this function just + * returns "normal", cachable memory. + * + * There are additional flags WEAK_ORDERING and WRITE_COMBINE to take + * into consideration here, too. All current known implementations of + * the OR1K support only strongly ordered memory accesses, so that flag + * is being ignored for now; uncached but write-combined memory is a + * missing feature of the OR1K. */ -void *or1k_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) +static void * +or1k_dma_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + struct dma_attrs *attrs) { unsigned long va; void *page; @@ -87,20 +101,23 @@ void *or1k_dma_alloc_coherent(struct device *dev, size_t size, va = (unsigned long)page; - /* - * We need to iterate through the pages, clearing the dcache for - * them and setting the cache-inhibit bit. - */ - if (walk_page_range(va, va + size, &walk)) { - free_pages_exact(page, size); - return NULL; + if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) { + /* + * We need to iterate through the pages, clearing the dcache for + * them and setting the cache-inhibit bit. + */ + if (walk_page_range(va, va + size, &walk)) { + free_pages_exact(page, size); + return NULL; + } } return (void *)va; } -void or1k_dma_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle) +static void +or1k_dma_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, struct dma_attrs *attrs) { unsigned long va = (unsigned long)vaddr; struct mm_walk walk = { @@ -108,16 +125,19 @@ void or1k_dma_free_coherent(struct device *dev, size_t size, void *vaddr, .mm = &init_mm }; - /* walk_page_range shouldn't be able to fail here */ - WARN_ON(walk_page_range(va, va + size, &walk)); + if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) { + /* walk_page_range shouldn't be able to fail here */ + WARN_ON(walk_page_range(va, va + size, &walk)); + } free_pages_exact(vaddr, size); } -dma_addr_t or1k_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - struct dma_attrs *attrs) +static dma_addr_t +or1k_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) { unsigned long cl; dma_addr_t addr = page_to_phys(page) + offset; @@ -147,16 +167,18 @@ dma_addr_t or1k_map_page(struct device *dev, struct page *page, return addr; } -void or1k_unmap_page(struct device *dev, dma_addr_t dma_handle, - size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) +static void +or1k_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) { /* Nothing special to do here... */ } -int or1k_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - struct dma_attrs *attrs) +static int +or1k_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) { struct scatterlist *s; int i; @@ -169,9 +191,10 @@ int or1k_map_sg(struct device *dev, struct scatterlist *sg, return nents; } -void or1k_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - struct dma_attrs *attrs) +static void +or1k_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) { struct scatterlist *s; int i; @@ -181,9 +204,10 @@ void or1k_unmap_sg(struct device *dev, struct scatterlist *sg, } } -void or1k_sync_single_for_cpu(struct device *dev, - dma_addr_t dma_handle, size_t size, - enum dma_data_direction dir) +static void +or1k_sync_single_for_cpu(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) { unsigned long cl; dma_addr_t addr = dma_handle; @@ -193,9 +217,10 @@ void or1k_sync_single_for_cpu(struct device *dev, mtspr(SPR_DCBIR, cl); } -void or1k_sync_single_for_device(struct device *dev, - dma_addr_t dma_handle, size_t size, - enum dma_data_direction dir) +static void +or1k_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) { unsigned long cl; dma_addr_t addr = dma_handle; @@ -205,6 +230,18 @@ void or1k_sync_single_for_device(struct device *dev, mtspr(SPR_DCBFR, cl); } +struct dma_map_ops or1k_dma_map_ops = { + .alloc = or1k_dma_alloc, + .free = or1k_dma_free, + .map_page = or1k_map_page, + .unmap_page = or1k_unmap_page, + .map_sg = or1k_map_sg, + .unmap_sg = or1k_unmap_sg, + .sync_single_for_cpu = or1k_sync_single_for_cpu, + .sync_single_for_device = or1k_sync_single_for_device, +}; +EXPORT_SYMBOL(or1k_dma_map_ops); + /* Number of entries preallocated for DMA-API debugging */ #define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index 6e61af8682b8..ddfcaa828b0e 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -1117,10 +1117,10 @@ ENTRY(sys_rt_sigreturn) ENTRY(sys_or1k_atomic) /* FIXME: This ignores r3 and always does an XCHG */ DISABLE_INTERRUPTS(r17,r19) - l.lwz r30,0(r4) - l.lwz r28,0(r5) - l.sw 0(r4),r28 - l.sw 0(r5),r30 + l.lwz r29,0(r4) + l.lwz r27,0(r5) + l.sw 0(r4),r27 + l.sw 0(r5),r29 ENABLE_INTERRUPTS(r17) l.jr r9 l.or r11,r0,r0 diff --git a/arch/openrisc/kernel/irq.c b/arch/openrisc/kernel/irq.c index 4bfead220956..e935b9d8eee1 100644 --- a/arch/openrisc/kernel/irq.c +++ b/arch/openrisc/kernel/irq.c @@ -14,17 +14,13 @@ * 2 of the License, or (at your option) any later version. */ -#include -#include #include #include #include #include #include -#include -#include #include - +#include #include /* read interrupt enabled status */ @@ -98,6 +94,7 @@ static void or1k_pic_mask_ack(struct irq_data *data) #endif } +#if 0 static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type) { /* There's nothing to do in the PIC configuration when changing @@ -107,43 +104,64 @@ static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type) return irq_setup_alt_chip(data, flow_type); } +#endif + +static struct irq_chip or1k_dev = { + .name = "or1k-PIC", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_ack, + .irq_mask_ack = or1k_pic_mask_ack, +}; + +static struct irq_domain *root_domain; static inline int pic_get_irq(int first) { - int irq; + int hwirq; - irq = ffs(mfspr(SPR_PICSR) >> first); + hwirq = ffs(mfspr(SPR_PICSR) >> first); + if (!hwirq) + return NO_IRQ; + else + hwirq = hwirq + first -1; - return irq ? irq + first - 1 : NO_IRQ; + return irq_find_mapping(root_domain, hwirq); } + +static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + irq_set_chip_and_handler_name(irq, &or1k_dev, + handle_level_irq, "level"); + irq_set_status_flags(irq, IRQ_LEVEL | IRQ_NOPROBE); + + return 0; +} + +static const struct irq_domain_ops or1k_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = or1k_map, +}; + +/* + * This sets up the IRQ domain for the PIC built in to the OpenRISC + * 1000 CPU. This is the "root" domain as these are the interrupts + * that directly trigger an exception in the CPU. + */ static void __init or1k_irq_init(void) { - struct irq_chip_generic *gc; - struct irq_chip_type *ct; + struct device_node *intc = NULL; + + /* The interrupt controller device node is mandatory */ + intc = of_find_compatible_node(NULL, NULL, "opencores,or1k-pic"); + BUG_ON(!intc); /* Disable all interrupts until explicitly requested */ mtspr(SPR_PICMR, (0UL)); - gc = irq_alloc_generic_chip("or1k-PIC", 1, 0, 0, handle_level_irq); - ct = gc->chip_types; - - ct->chip.irq_unmask = or1k_pic_unmask; - ct->chip.irq_mask = or1k_pic_mask; - ct->chip.irq_ack = or1k_pic_ack; - ct->chip.irq_mask_ack = or1k_pic_mask_ack; - ct->chip.irq_set_type = or1k_pic_set_type; - - /* The OR1K PIC can handle both level and edge trigged - * interrupts in roughly the same manner - */ -#if 0 - /* FIXME: chip.type??? */ - ct->chip.type = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_MASK; -#endif - - irq_setup_generic_chip(gc, IRQ_MSK(NR_IRQS), 0, - IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); + root_domain = irq_domain_add_linear(intc, 32, + &or1k_irq_domain_ops, NULL); } void __init init_IRQ(void) @@ -164,10 +182,3 @@ void __irq_entry do_IRQ(struct pt_regs *regs) irq_exit(); set_irq_regs(old_regs); } - -unsigned int irq_create_of_mapping(struct device_node *controller, - const u32 *intspec, unsigned int intsize) -{ - return intspec[0]; -} -EXPORT_SYMBOL_GPL(irq_create_of_mapping); diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c index a5dce82f864b..40f850e9766c 100644 --- a/arch/openrisc/mm/fault.c +++ b/arch/openrisc/mm/fault.c @@ -54,6 +54,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, struct vm_area_struct *vma; siginfo_t info; int fault; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; tsk = current; @@ -105,6 +106,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, if (in_interrupt() || !mm) goto no_context; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); @@ -143,6 +145,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, if (write_acc) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; + flags |= FAULT_FLAG_WRITE; } else { /* not present */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) @@ -159,7 +162,11 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, * the fault. */ - fault = handle_mm_fault(mm, vma, address, write_acc); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -167,11 +174,24 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, goto do_sigbus; BUG(); } - /*RGD modeled on Cris */ - if (fault & VM_FAULT_MAJOR) - tsk->maj_flt++; - else - tsk->min_flt++; + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + /*RGD modeled on Cris */ + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } + } up_read(&mm->mmap_sem); return;